回答編集履歴
2
コード中の説明(コメント)を追加しました
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
|
-
クロージャ
|
95
|
+
> `(*self.renderer)()`でクロージャ実行をしたいが共有参照`&mut`が関与しているのが原因なのか`F`のムーブが関与しているのが原因なのか、型サイズが不明なのが原因なのか、クロージャが呼べません。
|
91
96
|
|
92
|
-
|
97
|
+
`F`のムーブが関与しています。
|
93
98
|
|
99
|
+
クロージャーは捕捉した環境と無名関数からなるデーター構造です。`FnOnce`クロージャーをコールすると、環境の所有権が無名関数に移動し、消費されます。そのため1度だけコールできます。上のエラーは、環境を`self.render`フィールドから、無名関数へムーブしなければならないのに、`self`が不変参照`&`の先にあり`render`の値がムーブできないために起こっています。
|
100
|
+
|
94
|
-
エラーを解消するには、上のコードのように`Option::take`でムーブ
|
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の値は所有者がいる
|
137
|
+
Rustの値は所有者がいる間だけ存在できます。参照`&`は、誰か他に所有者がいる値を一時的に借用するためのものです。上のコードではクロージャーを作って、それの参照を`Card`に持たせています。しかし、クロージャーの所有者がいませんので、この式を抜けるとクロージャーがドロップされ、参照が無効(ダングリングポインター)になってしまいます。
|
127
138
|
|
128
139
|
エラーを解消するには、クロージャーを`Card`に所有させる必要があります。
|
129
140
|
|
1
サンプルコードを修正し、Appからジェネリック変数Fを削除しました
test
CHANGED
@@ -37,7 +37,7 @@
|
|
37
37
|
}
|
38
38
|
}
|
39
39
|
|
40
|
-
struct App
|
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
|
53
|
+
impl App {
|
56
54
|
pub fn new() -> Self {
|
57
55
|
Self {
|
58
56
|
presenter: Rc::new(Presenter),
|