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

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

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

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

解決済

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

JADEN
JADEN

総合スコア106

Rust

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

1回答

0リアクション

0クリップ

470閲覧

投稿2022/09/20 13:35

編集2022/09/21 00:03

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

Rust

impl Post { // --snip-- pub fn request_review(&mut self) { if let Some(s) = self.state.take() { self.state = Some(s.request_review()) } } } trait State { fn request_review(self: Box<Self>) -> Box<State>; } struct Draft {} impl State for Draft { fn request_review(self: Box<Self>) -> Box<State> { Box::new(PendingReview {}) } } struct PendingReview {} impl State for PendingReview { fn request_review(self: Box<Self>) -> Box<State> { self } }

分からない箇所は大きく分けて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

error[E0507]: cannot move out of `self.state.0` which is behind a mutable reference --> src\lib.rs:23:26 23 | if let Some(s) = self.state { | - ^^^^^^^^^^ help: consider borrowing here: `&self.state` | | | data moved here | 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

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

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

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

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

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

以下のような質問にはリアクションをつけましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

リアクションが多い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
86.12%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Rust

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