回答編集履歴

2

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

2022/04/05 23:36

投稿

tatsuya6502
tatsuya6502

スコア2046

test 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,11 +92,13 @@
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
 
99
+ クロージャーは捕捉した環境と無名関数からなるデーター構造です。`FnOnce`クロージャーをコールすると、環境の所有権が無名関数に移動し、消費されます。そのため1度だけコールできます。上のエラーは、環境を`self.render`フィールドから、無名関数へムーブしなければならないのに、`self`が不変参照`&`の先にあり`render`の値がムーブできないために起こっています。
100
+
94
- エラーを解消するには、上のコードのように`Option::take`でムーブ、`FnOnce`をやめて、`FnMut`などの他のクロージャーに変える必要があります。(`FnMut`は環境の可変参照`&mut`をとり、`Fn`は環境の不変参照`&`をとりますので、環境をムーブしません)
101
+ エラーを解消するには、上のコードのように`Option::take`でムーブする、ま、`FnOnce`をやめて、`FnMut`などの他のクロージャーに変える必要があります。(`FnMut`は環境の可変参照`&mut`をとり、`Fn`は環境の不変参照`&`をとりますので、環境をムーブしません)
95
102
 
96
103
  ```console
97
104
  error: lifetime may not live long enough
@@ -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,7 +130,11 @@
123
130
  | - temporary value is freed at the end of this statement
124
131
  ```
125
132
 
133
+ > ライフタイムの関係でクロージャ内の一時変数がドロップされるのが理由なのか、構造体にそのクロージャを持たせたいのですが上手くできません。
134
+
135
+ クロージャーに所有者がいないためにライフタイム関係のエラーになっています。
136
+
126
- Rustの値は所有者がいるときだけ存在できます。参照`&`は、誰か他に所有者がいる値を一時的に借用するためのものです。上のコードではクロージャーを作って、それの参照を`Card`に持たせています。しかし、クロージャーの所有者がいませんので、この式を抜けるとクロージャーがドロップされ、参照が無効(ダングリングポインター)になってしまいます。
137
+ Rustの値は所有者がいるだけ存在できます。参照`&`は、誰か他に所有者がいる値を一時的に借用するためのものです。上のコードではクロージャーを作って、それの参照を`Card`に持たせています。しかし、クロージャーの所有者がいませんので、この式を抜けるとクロージャーがドロップされ、参照が無効(ダングリングポインター)になってしまいます。
127
138
 
128
139
  エラーを解消するには、クロージャーを`Card`に所有させる必要があります。
129
140
 

1

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

2022/04/05 21:58

投稿

tatsuya6502
tatsuya6502

スコア2046

test 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),