teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

2

コード中の説明(コメント)を追加しました

2022/04/05 23:36

投稿

tatsuya6502
tatsuya6502

スコア2055

answer CHANGED
@@ -15,7 +15,9 @@
15
15
  }
16
16
 
17
17
  struct Card<F> {
18
- // クロージャーを参照として持つのではなく、所有する
18
+ // クロージャーを借用するのではなく、所有する
19
+ // Rustの値(クロージャーなど)は所有者がいないと存在できないため、誰かが所有者に
20
+ // なる必要がある
19
21
  // また、FnOnceは1回しかコールできないので、Option型にして、コール時にNoneにする
20
22
  renderer: Option<F>,
21
23
  }
@@ -32,7 +34,8 @@
32
34
  // Option型のtakeメソッドは、参照先の値をNoneと交換することで、
33
35
  // 値をムーブアウトさせる。(self.rendererがNoneになる)
34
36
  let renderer = self.renderer.take().expect("renderer is none");
35
- // クロージャーをコールする(消費する)
37
+ // クロージャーをコールする
38
+ // FnOnceクロージャーは環境を無名関数内へムーブして消費する
36
39
  renderer()
37
40
  }
38
41
  }
@@ -47,6 +50,8 @@
47
50
  // selfから始まる参照の連鎖の先にselfを含めることはできない
48
51
  // Rcを使って、self.presenterとcard.redererの両方をPresenterの所有者にさせる
49
52
  presenter: Rc<Presenter>,
53
+ // クロージャーの型はコンパイラーが自動生成する匿名型になるので、ここでFの型に
54
+ // その具象型を指定できない。トレイトオブジェクト型にしておく
50
55
  card: Card<Box<dyn FnMut() -> String>>,
51
56
  }
52
57
 
@@ -87,12 +92,14 @@
87
92
  24 | (*self.renderer)()
88
93
  ```
89
94
 
90
- クロージャーは捕捉した環境と無名数からなデーター構造です。`FnOnce`クロージャーをコールすると、環境所有権無名数に移動し、消費されます。そため1度だけコルでき
95
+ > `(*self.renderer)()`でクロージャ実行をしたいが共有参照`&mut`が与していのが原因なのか`F`のムーブが関ているのが原因なのか型サイズが不明なが原因なのか、クロジャが呼べせん
91
96
 
92
- 上のエラーは、環境を`self.render`フィールドから、無名関数へムーブしなければならないに、`self`が不変参照`&`の先にありムーブできないために起こっています。
97
+ `F`のムーブが関与しています。
93
98
 
94
- エラを解消するには、上のコドのように`Option::take`でムーブしたり、`FnOnce`をやめて、`FnMut`などの他のクロージャーに変え必要あります。(`FnMut`は環境の可変参照`&mut`をとり、`Fn`は環境の不変参照`&`をとますで、環境をムーブせん)
99
+ クロジャは捕捉した環境と無名関数からなるデーター構造です。`FnOnce`クロージャーをコールすと、環境の所有権無名関数に移動し、消費されます。そのため1度だけコールできます。上のエラー環境`self.render`フィールドから無名関数へムーブしなければならないのに、`self`不変参照`&`の先にあ`render`値がムーブできないために起こっていす。
95
100
 
101
+ エラーを解消するには、上のコードのように`Option::take`でムーブする、または、`FnOnce`をやめて、`FnMut`などの他のクロージャーに変える必要があります。(`FnMut`は環境の可変参照`&mut`をとり、`Fn`は環境の不変参照`&`をとりますので、環境をムーブしません)
102
+
96
103
  ```console
97
104
  error: lifetime may not live long enough
98
105
  --> src/main.rs:41:31
@@ -105,7 +112,7 @@
105
112
 
106
113
  `self.card`に、`self.presenter`という自分(`self`)への参照をもつクロージャーをセットしようとしていますが、SafeなRustでは、このような自己参照構造体を作れません。それぞれの`&self`が異なるライフタイムを持つことが求められ、矛盾が生じてコンパイルエラーになります。
107
114
 
108
- エラーを解消するには、上のコードのように`Rc`など自己参照を断ち切る必要があります。
115
+ エラーを解消するには、上のコードのように`Rc`などを使って自己参照を断ち切る必要があります。
109
116
 
110
117
  ```console
111
118
  error[E0716]: temporary value dropped while borrowed
@@ -123,6 +130,10 @@
123
130
  | - temporary value is freed at the end of this statement
124
131
  ```
125
132
 
126
- Rust値は所有者がいるときだけ存在きます。参照`&`は、誰か他に所有者がいる値を一時的に借用すためです。上コードではクロージャ作って、それの参照を`Card`に持たせます。しかし、クロージャー所有者ませんので、この式を抜けるとクロージャーがドロップされ、参照が無効(ダングリングポインター)になってしまいます
133
+ > ライフタイム関係クロージャ内の一時変数がドロップされるのが理由なか、構造体にそのクロージャを持たせいのです上手くできません。
127
134
 
135
+ クロージャーに所有者がいないためにライフタイム関係のエラーになっています。
136
+
137
+ Rustの値は所有者がいる間だけ存在できます。参照`&`は、誰か他に所有者がいる値を一時的に借用するためのものです。上のコードではクロージャーを作って、それの参照を`Card`に持たせています。しかし、クロージャーの所有者がいませんので、この式を抜けるとクロージャーがドロップされ、参照が無効(ダングリングポインター)になってしまいます。
138
+
128
139
  エラーを解消するには、クロージャーを`Card`に所有させる必要があります。

1

サンプルコードを修正し、Appからジェネリック変数Fを削除しました

2022/04/05 21:58

投稿

tatsuya6502
tatsuya6502

スコア2055

answer CHANGED
@@ -37,7 +37,7 @@
37
37
  }
38
38
  }
39
39
 
40
- struct App<F> {
40
+ struct App {
41
41
  // Rcは参照カウント式のスマートポインター
42
42
  // Rcを使うと1つの値に対して複数の所有者を持たせられる
43
43
  // ここでは、所有者の一人はApp、もう一人はCardが持つrendererクロージャーになる
@@ -47,12 +47,10 @@
47
47
  // selfから始まる参照の連鎖の先にselfを含めることはできない
48
48
  // Rcを使って、self.presenterとcard.redererの両方をPresenterの所有者にさせる
49
49
  presenter: Rc<Presenter>,
50
- card: Card<F>,
50
+ card: Card<Box<dyn FnMut() -> String>>,
51
51
  }
52
52
 
53
- // このAppではクロージャーの型Fが一意に定まらないので、Box<dyn ..> として
54
- // トレイトオブジェクトにする必要がある
55
- impl App<Box<dyn FnMut() -> String>> {
53
+ impl App {
56
54
  pub fn new() -> Self {
57
55
  Self {
58
56
  presenter: Rc::new(Presenter),