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

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

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

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

Q&A

解決済

1回答

822閲覧

未代入フィールドを許可しない構造体とトレイトメソッドのselfムーブの挙動(Rustドキュメントのサンプルコード)が理解できません

JADEN

総合スコア106

Rust

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

0グッド

0クリップ

投稿2022/09/20 13:35

編集2022/09/21 00:03

下記のRustドキュメントのリスト17-15のコードが一部理解できないので教えてほしいです。
The Rust Programming Language 日本語版 - オブジェクト指向デザインパターンを実装する#記事の査読を要求すると、状態が変化する

Rust

1impl Post { 2 // --snip-- 3 pub fn request_review(&mut self) { 4 if let Some(s) = self.state.take() { 5 self.state = Some(s.request_review()) 6 } 7 } 8} 9 10trait State { 11 fn request_review(self: Box<Self>) -> Box<State>; 12} 13 14struct Draft {} 15 16impl State for Draft { 17 fn request_review(self: Box<Self>) -> Box<State> { 18 Box::new(PendingReview {}) 19 } 20} 21 22struct PendingReview {} 23 24impl State for PendingReview { 25 fn request_review(self: Box<Self>) -> Box<State> { 26 self 27 } 28}

分からない箇所は大きく分けて2つです。

  1. impl Postrequest_reviewメソッドでSome(s)self.state.take()を使用している理由
  2. Stateトレイトのrequest_reviewメソッドの引数がself: Box<Self>の理由

1番目について、3つの疑問に分かれます。

まず、self.state = self.state.request_review();のように直接代入するコードはなぜだめなのでしょうか。ドキュメントでは、構造体は未代入のフィールドを許可しないから先述のコードは動作しないと記載があるのですが、未代入のフィールドなどないように思えるので、意味が良く分かりませんでした。

また、Some(s)を使用する方のコード(元のコード)に関しても、self.state.take()の代わりにself.stateを使えばSome(s)でムーブすると思ったのですが、実際には下記の様なムーブできないエラーになりました。なぜなのでしょうか。

Rust

1error[E0507]: cannot move out of `self.state.0` which is behind a mutable reference 2--> src\lib.rs:23:26 323 | if let Some(s) = self.state { 4| - ^^^^^^^^^^ help: consider borrowing here: `&self.state` 5| | 6| data moved here 7| move occurs because `s` has type `Box<dyn State>`, which does not implement the `Copy` trait

さらに、上記のエラーから参照なら大丈夫かと思いself.state.take()の代わりに&self.stateを使用してみたのですが(前提としてBoxへの参照が許されているのか分かりませんが)、下記のようなエラーになりました。s.request_review()でムーブが発生するとなぜダメなのでしょうか(冒頭に提示した大きく分けた2つ目の疑問でも言及しているのですが、そもそもなぜここでムーブが発生するのか良く分かりません)。

Rust

1error[E0507]: cannot move out of `*s` which is behind a shared reference 2--> src\lib.rs:24:31 324 | self.state = Some(s.request_review()) 4| ^^---------------- 5| | | 6| | `*s` moved due to this method call 7| move occurs because `*s` has type `Box<dyn State>`, which does not implement the `Copy` trait 8note: this function takes ownership of the receiver `self`, which moves `*s` 9--> src\lib.rs:30:23 1030 | fn request_review(self: Box<Self>) -> Box<dyn State>; 11| ^^^^

2番目について、2つの疑問に分かれます。

まず、なぜ&selfではなく、selfであるのでしょうか。ドキュメントではBox<Self>の所有権を奪い、古い状態を無効化するためと記載があるのですが、この文章の意味が良く分かりませんでした。s.request_review()が実行されると、メソッド実行開始の時点でsの所有権がselfにムーブし、そのままメソッドの終わりでドロップするので消滅するという意味でしょうか?また、なぜ古い状態を無効化する必要があるのでしょうか。単に新しい状態を戻り値で返すだけでよいのではと思ったのですが。(不要になったBoxのメモリが長く残ってしまうのを防ぐためでしょうか?)

次に、Box<Self>の指定が必要な理由です。ドキュメントではBox<Self>にすることで、Boxに格納された状態からのみ呼び出せるようになると記載がありますが、なぜその制限が必要なのでしょうか。単純に誤った使い方をした場合にコンパイルエラーを出すためでしょうか。

長くなってしまいますが、よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

Some(s)self.state.take() を使用している理由

メンバをムーブして所有権を手放すとそのメンバを含む構造体全体も所有権を失います。 一部だけが欠けた (未代入な) 状態を許さないので全体が失われるということです。

request_review では self&mut として借りているだけなので所有権を渡すということは許されず、全体として整合性が取れないということになります。 なので take を用いて仮の値と入れ替える形をとる必要があるわけですね。

request_review メソッドの引数が self: Box<Self> の理由

まず、なぜ &self ではなく、 self であるのでしょうか。 ドキュメントでは Box<Self> の所有権を奪い、古い状態を無効化するためと記載があるのですが、この文章の意味が良く分かりませんでした。 s.request_review() が実行されると、メソッド実行開始の時点で s の所有権が self にムーブし、そのままメソッドの終わりでドロップするので消滅するという意味でしょうか?

その通りです。 所有権を奪っておきながらそのオブジェクトを放棄すれば消滅します。

また、なぜ古い状態を無効化する必要があるのでしょうか。

Box<Self> の指定が必要な理由です。

これは必要というより前提条件ですね。

例として書かれているものなので根本的な設計上の必要性を追求しても意味がないです。 必要性があってやっているというよりは必要なときはこう書けるという説明だと考えてください。

投稿2022/09/21 00:06

SaitoAtsushi

総合スコア5446

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

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

JADEN

2022/09/21 12:42

回答を読み、2点とも疑問を解決することができました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問