質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

2回答

4771閲覧

Optionalのmatch地獄から脱出したい

yumetodo

総合スコア5850

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

1グッド

0クリップ

投稿2017/12/11 01:31

編集2017/12/11 09:09

普段C++書いていて今回Rustに挑戦しています。

rust

1struct TypeElement<'a> { 2 before: Option<&'a str>, 3 after: Option<&'a str>, 4 value: Option<&'a str>, 5}

のようなstructに対してPartialEqを実装しようとしています。

  1. valueが双方値を持つなら値の比較結果
  2. (beforeが双方値を持つならその値の比較結果) かつ (afterが双方値を持つなら値の比較結果、双方持たないなら真)
  3. その他は偽

という条件を書こうとした結果、matchがネストしすぎた地獄を産んでしまいました。

rust

1impl<'a> PartialEq<TypeElement<'a>> for TypeElement<'a> { 2 fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 3 match self.value { 4 Some(l) => { 5 match other.value { 6 // check `value` 7 Some(r) => l.eq(r), 8 None => false, 9 } 10 }, 11 None => { 12 match other.value { 13 Some(_) => false, 14 // check `before`, `after` 15 None => { 16 match self.before { 17 Some(b1) => { 18 match other.before { 19 Some(b2) => { 20 b1.eq(b2) && match self.after { 21 Some(a1) => { 22 match other.after { 23 Some(a2) => a1.eq(a2), 24 None => false, 25 } 26 }, 27 None => { 28 match other.after { 29 Some(_) => false, 30 None => true, 31 } 32 }, 33 } 34 } 35 None => false 36 } 37 }, 38 None => false, 39 } 40 }, 41 } 42 }, 43 } 44 } 45}

C++17のstd::optionaloperator==があるのですが、Rustにはそれに該当するものを見つけられませんでした。

もうすこし可読性のある書き方をするにはどうしたらいいでしょうか?


追記

gyu-donさんの回答を受けて

rust

1impl<'a> PartialEq<TypeElement<'a>> for TypeElement<'a> { 2 fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 3 if self.value.is_some() { 4 self.value == other.value 5 } else { 6 self.before.is_some() && self.before == other.before && self.after == other.after 7 } 8 } 9}

のように書き換えたのですが、8e6aa6f

> cargo test Compiling cpp_template_type_indenter_lib v0.1.0 (file:///C:/Users/yumetodo/Documents/rust/cpp_template_type_indenter_lib) error[E0308]: mismatched types --> src\type_element.rs:19:83 | 19 | self.before.is_some() && self.before == other.before && self.after == other.after | ^^^^^^^^^^^ lifetime mismatch | = note: expected type `std::option::Option<&'a str>` found type `std::option::Option<&'b str>` note: the lifetime 'b as defined on the method body at 15:5... --> src\type_element.rs:15:5 | 15 | / fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 16 | | if self.value.is_some() { 17 | | self.value == other.value 18 | | } else { 19 | | self.before.is_some() && self.before == other.before && self.after == other.after 20 | | } 21 | | } | |_____^ note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 14:1 --> src\type_element.rs:14:1 | 14 | / impl<'a> PartialEq<TypeElement<'a>> for TypeElement<'a> { 15 | | fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 16 | | if self.value.is_some() { 17 | | self.value == other.value ... | 21 | | } 22 | | } | |_^ error: aborting due to previous error(s) error[E0308]: mismatched types --> src\type_element.rs:19:83 | 19 | self.before.is_some() && self.before == other.before && self.after == other.after | ^^^^^^^^^^^ lifetime mismatch | = note: expected type `std::option::Option<&'a str>` found type `std::option::Option<&'b str>` note: the lifetime 'b as defined on the method body at 15:5... --> src\type_element.rs:15:5 | 15 | / fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 16 | | if self.value.is_some() { 17 | | self.value == other.value 18 | | } else { 19 | | self.before.is_some() && self.before == other.before && self.after == other.after 20 | | } 21 | | } | |_____^ note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 14:1 --> src\type_element.rs:14:1 | 14 error:| /Could not compile `cpp_template_type_indenter_lib`. impl<'a> PartialEq<TypeElement<'a>> for TypeElement<'a> { 15Build failed, waiting for other jobs to finish... | | fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 16 | | if self.value.is_some() { 17 | | self.value == other.value ... | 21 | | } 22 | | } | |_^ error: aborting due to previous error(s) error: Could not compile `cpp_template_type_indenter_lib`. To learn more, run the command again with --verbose.

のように怒られます。

tatsuya6502👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

タプルを使うと2者をまとめてmatchに掛けることができます。

rust

1 2struct TypeElement<'a> { 3 before: Option<&'a str>, 4 after: Option<&'a str>, 5 value: Option<&'a str>, 6} 7 8 9impl<'a> PartialEq for TypeElement<'a> { 10 fn eq<'b>(&self, other: &TypeElement<'b>) -> bool { 11 match (self.value, other.value) { 12 (Some(l), Some(r)) => return l == r, 13 // pass through 14 (None, None) => (), 15 (_, _) => return false, 16 }; 17 // if both of `self` and `other` don't have values, 18 // then compare `before`s and `after`s 19 let before = match (self.before, other.before) { 20 (Some(b1), Some(b2)) => b1 == b2, 21 (_, _) => false, 22 }; 23 let after = match (self.after, other.after) { 24 (Some(a1), Some(a2)) => a1 == a2, 25 (None, None) => true, 26 (_, _) => false, 27 }; 28 before && after 29 } 30}

beforeとafterをそれぞれ比較したあとで&&を取っているのでショートサーキットされてませんが必要ならばreturnで早期にリターンすることも可能です。

投稿2017/12/11 08:41

blackenedgold

総合スコア468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2017/12/11 09:18

横道にそれますが、PartialEq<TypeElement<'a>>ではなくPartialEqだけでいいのはどういう仕組によるものでしょうか?
blackenedgold

2017/12/11 09:43

型パラメータにデフォルト値が設定されているからです。 https://doc.rust-lang.org/std/cmp/trait.PartialEq.html ところで今更気づいたのですが`eq`にライフタイムパラメータが付けられるのはちょっと変ですね。 ```rust impl<'a> PartialEq for TypeElement<'a> { fn eq(&self, other: &TypeElement<'a>) -> bool { ... } } ``` のようにかかないといけない筈ですが、隠れた仕様なのかコンパイラのバグなのか即断出来ないです。 念の為上記のようにライフタイムパラメータなしで書くことをおすすめします。
yumetodo

2017/12/11 11:01

あれっ、ライフタイムが違うstrって比較できない・・・?
blackenedgold

2017/12/11 14:20

形式上はそうですが`& T`のライフタイムはRustコンパイラが自動で調整してくれるので多くの場合は気にしなくて大丈夫です。 調整というのは長い方のライフタイムを短い方に合わせるので上記'aと'bのようにどちらが長いか決められないときにエラーになります。 参考 https://doc.rust-lang.org/src/core/str/mod.rs.html#1611-1618
guest

0

C++17のstd::optionalはoperator==があるのですが、Rustにはそれに該当するものを見つけられませんでした。

https://doc.rust-lang.org/std/option/enum.Option.html
impl<T> PartialEq<Option<T>> for Option<T> where T: PartialEq<T>
とあるので、TPartialEqならば、Option<T>PartialEqになってます。

https://play.rust-lang.org/?gist=1b3aacc49fc6ac4f2b2b2a3f6acd286b&version=stable

のとおり、
双方がSome(x)ならばx同士の比較結果が返って、双方がNoneならばtrueが返る挙動のようです。
(現時点でのOptionのソースを読んでみると、derive(PartialEq)で定義されているので、ここのenumの場合の挙動を示します)

None同士でtrueになるのを避けつつ比較するには、 self.value.is_some() && self.value == other.value のようにしたらいいかと思います。

投稿2017/12/11 02:09

編集2017/12/11 02:10
gyu-don

総合スコア16

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2017/12/11 02:13

あああ、リファレンス見落としていた・・・。試してみます。
yumetodo

2017/12/11 09:10

試してみましたがlifetimeが合わないとか怒られます・・・
gyu-don

2017/12/11 17:37

ちゃんと試さずにいい加減なこと書いてすみません・・・・。 ```rust fn main() { let longlife = Some("aaa"); let aaa = "aaa".to_owned(); { let shortlife = Some(aaa.as_str()); // ok shortlife == longlife; // ng longlife == shortlife; } } ``` たしかにOptionの比較怪しいですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問