回答編集履歴

1

誤字を修正しました

2021/05/02 04:31

投稿

tatsuya6502
tatsuya6502

スコア2046

test CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
 
10
10
 
11
- `FnMut`トレイトを実装するときは、そのクロージャ束縛されている変数(`func`)に`mut`キーワードが必要になります。
11
+ `FnMut`トレイトを実装するときは、そのクロージャ束縛されている変数(`func`)に`mut`キーワードが必要になります。
12
12
 
13
13
 
14
14
 
@@ -16,7 +16,7 @@
16
16
 
17
17
 
18
18
 
19
- クロージャは「無名関数」と「捕捉した環境」からなるデータ構造です。Rustではクロージャを作る構文(例:`|| {..}`)は糖衣構文になっており、コンパイラはそれに対して以下を自動生成します。
19
+ クロージャは「無名関数」と「捕捉した環境」からなるデータ構造です。Rustではクロージャを作る構文(`|| {..}`)は糖衣構文になっており、コンパイラはそれに対して以下を自動生成します。
20
20
 
21
21
 
22
22
 
@@ -56,6 +56,186 @@
56
56
 
57
57
  ```rust
58
58
 
59
+ struct FuncEnv1<'a> {
60
+
61
+ count: &'a mut i32,
62
+
63
+ str: String,
64
+
65
+ }
66
+
67
+ ```
68
+
69
+
70
+
71
+ クロージャトレイトは、無名関数が環境をどう使うかによって、以下の3種類に分けられています。
72
+
73
+
74
+
75
+ `FnOnce`トレイト([ドキュメント](https://doc.rust-lang.org/std/ops/trait.FnOnce.html)):
76
+
77
+
78
+
79
+ - 無名関数を表す`call_once(self, args: Args)`メソッドの実装を要求する
80
+
81
+ - このメソッドの第1引数は`self`なので環境の所有権をとる。そのため1回しかコールできない
82
+
83
+
84
+
85
+ `FnMut`トレイト([ドキュメント](https://doc.rust-lang.org/std/ops/trait.FnMut.html)):
86
+
87
+
88
+
89
+ - 無名関数を表す`call_mut(&mut self, args: Args)`メソッドの実装を要求する
90
+
91
+ - このメソッドの第1引数は`&mut self`なので何度でもコールできるが、環境の内容を変更することがある
92
+
93
+ - `FnMut`トレイトを実装するには、`FnOnce`トレイトも実装しなければならない
94
+
95
+
96
+
97
+ `Fn`トレイト([ドキュメント](https://doc.rust-lang.org/std/ops/trait.Fn.html)):
98
+
99
+
100
+
101
+ - 無名関数を表す`call(&self, args: Args)`メソッドの実装を要求する
102
+
103
+ - このメソッドの第1引数は`&self`なので何度でもコールできる。環境の内容は変更しない
104
+
105
+ - `Fn`トレイトを実装するには、`FnMut`と`FnOnce`トレイトも実装しなければならない
106
+
107
+
108
+
109
+ 最初に言ったとおり、例1のクロージャと例2のクロージャでは実装しているトレイトが異なります。
110
+
111
+
112
+
113
+ - 例1のクロージャ:`FnOnce`トレイトだけを実装している
114
+
115
+ - 例2のクロージャ:`FnOnce`トレイトと`FnMut`トレイトの両方を実装している
116
+
117
+
118
+
119
+ このことを確認してみましょう。例1を以下のように書き換えます。
120
+
121
+
122
+
123
+ ```rust
124
+
125
+ // 例1の修正版
126
+
127
+
128
+
129
+ // この関数は引数にFnMutを実装するクロージャをとる
130
+
131
+ fn take_fn_mut(func: &mut impl FnMut()) {
132
+
133
+ func();
134
+
135
+ }
136
+
137
+
138
+
139
+ // この関数は引数にFnOnceを実装するクロージャをとる
140
+
141
+ fn take_fn_once(func: impl FnOnce()) {
142
+
143
+ func();
144
+
145
+ }
146
+
147
+
148
+
149
+ fn main() {
150
+
151
+ let str = "string".to_string();
152
+
153
+ let mut count = 10;
154
+
155
+
156
+
157
+ let func = || {
158
+
159
+ count += 1;
160
+
161
+ std::mem::drop(str);
162
+
163
+ };
164
+
165
+
166
+
167
+ // FnMutではないのでコンパイルできない
168
+
169
+ take_fn_mut(&mut func);
170
+
171
+
172
+
173
+ // FnOnceなのでコンパイルできる
174
+
175
+ take_fn_once(func);
176
+
177
+ }
178
+
179
+ ```
180
+
181
+
182
+
183
+ このコードは`take_fn_mut(&mut func);`のところで以下のようなコンパイルエラーになります。
184
+
185
+
186
+
187
+ ```console
188
+
189
+ $ cargo check
190
+
191
+ ...
192
+
193
+
194
+
195
+ error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
196
+
197
+ --> src/bin/main1.rs:17:16
198
+
199
+ |
200
+
201
+ 17 | let func = || {
202
+
203
+ | ^^ this closure implements `FnOnce`, not `FnMut`
204
+
205
+ 18 | count += 1;
206
+
207
+ 19 | std::mem::drop(str);
208
+
209
+ | --- closure is `FnOnce` because it moves the variable `str` out of its environment
210
+
211
+ ...
212
+
213
+ 23 | take_fn_mut(&mut func);
214
+
215
+ | ----------- the requirement to implement `FnMut` derives from here
216
+
217
+ ```
218
+
219
+
220
+
221
+ **エラーの内容**
222
+
223
+
224
+
225
+ - クロージャが`FnMut`を実装していることが期待されているが、実際には`FnOnce`しか実装していない
226
+
227
+ - クロージャは`FnOnce`を実装する(`FnMut`ではない)
228
+
229
+ - クロージャが`FnOnce`なのは、変数`str`を環境(の匿名`struct`)からメソッド本体にムーブアウトするから
230
+
231
+
232
+
233
+ 先ほど示したように、例1の匿名型は以下のようになります。
234
+
235
+
236
+
237
+ ```rust
238
+
59
239
  struct MyFuncEnv<'a> {
60
240
 
61
241
  count: &'a mut i32,
@@ -68,61 +248,109 @@
68
248
 
69
249
 
70
250
 
71
- クロージャトレイトは、無名関数が環境どう使うによって、以下の3種類に分けられてい
72
-
73
-
74
-
75
- `FnOnce`トレイト([ドキュメント](https://doc.rust-lang.org/std/ops/trait.FnOnce.html)):
76
-
77
-
78
-
79
- - 無名関数を表す`call_once(self, args: Args)`メソッドの実装を要求する
80
-
81
- - メソッドの第1引数は`self`なので環境の所有権をとる。そのため1回しかコールできない
82
-
83
-
84
-
85
- `FnMut`トレイト([ドキュメント](https://doc.rust-lang.org/std/ops/trait.FnMut.html)):
86
-
87
-
88
-
89
- - 無名関数を表す`call_mut(&mut self, args: Args)`メソッドの実装要求する
90
-
91
- - このメソッドの第1引数は`&mut self`なので何度でもコールできるが、環境の内容を変更することがある
92
-
93
- - `FnMut`トレイトを実装するには、`FnOnce`トレイトも実装しなければならない
94
-
95
-
96
-
97
- `Fn`トレイト([ドキュメント](https://doc.rust-lang.org/std/ops/trait.Fn.html)):
98
-
99
-
100
-
101
- - 無名関数を表す`call(&self, args: Args)`メソッドの実装を要求する
102
-
103
- - このメソッドの第1引数は`&self`なので何度でもコールできる。環境の内容は変更しない
104
-
105
- - `Fn`トレイトを実装するには、`FnMut`と`FnOnce`トレイトも実装しなければならない
106
-
107
-
108
-
109
- 最初に言ったとおり、例1のクロージャと例2のクロージャでは実装しているトレイトが異なります。
110
-
111
-
112
-
113
- - 例1のクロージャ:`FnOnce`トレイトだけを実装している
114
-
115
- - 例2のクロージャ`FnOnce`トレイトと`FnMut`トレイト両方を実装している
116
-
117
-
118
-
119
- このことを確認してみましょう。例1を以下のように書き換えます。
120
-
121
-
122
-
123
- ```rust
124
-
125
- // 例1の修正版
251
+ `std::mem::drop``self.str`の所有権を取るため、無名関数は第1引数に`self`を取る必要あります(`&mut self`や`&self`ではだめです) そのため、このクロージャは`call_once(self, args: Args)`の実装持つ`FnOnce`し実装できせん
252
+
253
+
254
+
255
+ 一方、例2のクロージャは`FnOnce`に加えて`FnMut`を実装します。そのため以下のように書き換えてもコンパイルエラーになりません。
256
+
257
+
258
+
259
+ ```rust
260
+
261
+ // 例2修正版
262
+
263
+ fn main() {
264
+
265
+ let mut count = 10;
266
+
267
+
268
+
269
+ // mutを追加した
270
+
271
+ let mut func = || {
272
+
273
+ count += 1;
274
+
275
+ };
276
+
277
+
278
+
279
+ // どちらもコンパイルできる
280
+
281
+ take_fn_mut(&mut func);
282
+
283
+ take_fn_once(func);
284
+
285
+ }
286
+
287
+ ```
288
+
289
+
290
+
291
+ クロージャの本体から`std::mem::drop`がなくなったために、所有権を取る必要がなくなり、`FnMut`が要求する`call_mut(&mut self, args: Args)`が実装できるようになったからです。
292
+
293
+
294
+
295
+ 最後に`FnMut`だと`func`に`mut`が必要になる理由ですが、これはクロージャが匿名`struct`であることで説明できます。現時点はnightly版コンパイラ限定ですが、クロージャ`struct`と`FnMut`など実装自分で書くこともできます。それを使うと例2は以下のように実装できます。(`cargo +nightly run`で実行できます)
296
+
297
+
298
+
299
+ ```rust
300
+
301
+ // `FnMut`などの実装を提供するには、現時点ではnightly版コンパイラで、
302
+
303
+ // 以下の実験的なフィーチャをオンにしないといけない
304
+
305
+ #![feature(fn_traits, unboxed_closures)]
306
+
307
+
308
+
309
+ // 例2のクロージャの環境。countの&mut参照を持つ
310
+
311
+ struct FuncEnv2<'a> {
312
+
313
+ count: &'a mut i32,
314
+
315
+ }
316
+
317
+
318
+
319
+ // FnOnceの実装
320
+
321
+ // FnMutを実装するためには、まずFnOnceを実装しないといけない
322
+
323
+ impl<'a> FnOnce<()> for FuncEnv2<'a> {
324
+
325
+ type Output = (); // クロージャの戻り値型
326
+
327
+
328
+
329
+ // call_onceを実装する。このメソッドはselfを取る
330
+
331
+ extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
332
+
333
+ *self.count += 1;
334
+
335
+ }
336
+
337
+ }
338
+
339
+
340
+
341
+ // FnMutの実装
342
+
343
+ impl<'a> FnMut<()> for FuncEnv2<'a> {
344
+
345
+ // call_mutを実装する。このメソッドは&mut selfを取る
346
+
347
+ extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
348
+
349
+ *self.count += 1;
350
+
351
+ }
352
+
353
+ }
126
354
 
127
355
 
128
356
 
@@ -148,30 +376,32 @@
148
376
 
149
377
  fn main() {
150
378
 
151
- let str = "string".to_string();
152
-
153
379
  let mut count = 10;
154
380
 
155
381
 
156
382
 
383
+ // funcの実体は環境を表すstruct
384
+
157
- let func = || {
385
+ let mut func = FuncEnv2 {
158
-
386
+
159
- count += 1;
387
+ count: &mut count,
160
-
161
- std::mem::drop(str);
162
388
 
163
389
  };
164
390
 
165
391
 
166
392
 
393
+ // FnMutを実装するのでcall_mut(&mut self, ...)を呼ぼうとする
394
+
395
+ // &mut self(&mut funcと同じ)をするためにはfuncがmutでないとならない
396
+
397
+ func();
398
+
399
+
400
+
167
- // FnMutではいのでコンパイルできない
401
+ // これらも問題く呼べる
168
402
 
169
403
  take_fn_mut(&mut func);
170
404
 
171
-
172
-
173
- // FnOnceなのでコンパイルできる
174
-
175
405
  take_fn_once(func);
176
406
 
177
407
  }
@@ -180,236 +410,6 @@
180
410
 
181
411
 
182
412
 
183
- このコードは`take_fn_mut(&mut func);`のところで以下のようなコンパイルエラーになります。
184
-
185
-
186
-
187
- ```console
188
-
189
- $ cargo check
190
-
191
- ...
192
-
193
-
194
-
195
- error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
196
-
197
- --> src/bin/main1.rs:17:16
198
-
199
- |
200
-
201
- 17 | let func = || {
202
-
203
- | ^^ this closure implements `FnOnce`, not `FnMut`
204
-
205
- 18 | count += 1;
206
-
207
- 19 | std::mem::drop(str);
208
-
209
- | --- closure is `FnOnce` because it moves the variable `str` out of its environment
210
-
211
- ...
212
-
213
- 23 | take_fn_mut(&mut func);
214
-
215
- | ----------- the requirement to implement `FnMut` derives from here
216
-
217
- ```
218
-
219
-
220
-
221
- **エラーの内容**
222
-
223
-
224
-
225
- - クロージャが`FnMut`を実装していることが期待されているが、実際には`FnOnce`しか実装していない
226
-
227
- - クロージャは`FnOnce`を実装する(`FnMut`ではない)
228
-
229
- - クロージャが`FnOnce`なのは、変数`str`を環境(の匿名`struct`)からメソッド本体にムーブアウトするから
230
-
231
-
232
-
233
- 先ほど示したように、例1の匿名型は以下のようになります。
234
-
235
-
236
-
237
- ```rust
238
-
239
- struct MyFuncEnv<'a> {
240
-
241
- count: &'a mut i32,
242
-
243
- str: String,
244
-
245
- }
246
-
247
- ```
248
-
249
-
250
-
251
- `std::mem::drop`は`self.str`の所有権を取るため、無名関数は第1引数に`self`を取る必要があります(`&mut self`や`&self`ではだめです) そのため、このクロージャは`call_once(self, args: Args)`の実装を持つ`FnOnce`しか実装できません。
252
-
253
-
254
-
255
- 一方、例2のクロージャは`FnOnce`に加えて`FnMut`を実装します。そのため以下のように書き換えてもコンパイルエラーになりません。
256
-
257
-
258
-
259
- ```rust
260
-
261
- // 例2の修正版
262
-
263
- fn main() {
264
-
265
- let mut count = 10;
266
-
267
-
268
-
269
- // mutを追加した
270
-
271
- let mut func = || {
272
-
273
- count += 1;
274
-
275
- };
276
-
277
-
278
-
279
- // どちらもコンパイルできる
280
-
281
- take_fn_mut(&mut func);
282
-
283
- take_fn_once(func);
284
-
285
- }
286
-
287
- ```
288
-
289
-
290
-
291
- クロージャの本体から`std::mem::drop`がなくなったために、所有権を取る必要がなくなり、`FnMut`が要求する`call_mut(&mut self, args: Args)`が実装できるようになったからです。
292
-
293
-
294
-
295
- 最後に`FnMut`だと`func`に`mut`が必要になる理由ですが、これはクロージャが匿名`struct`であることで説明できます。現時点はnightly版のコンパイラ限定ですが、クロージャの`struct`と`FnMut`などの実装を自分で書くこともできます。それを使うと例2は以下のように実装できます。(`cargo +nightly run`で実行できます)
296
-
297
-
298
-
299
- ```rust
300
-
301
- // `FnMut`などの実装を提供するには、現時点ではnightly版コンパイラで、
302
-
303
- // 以下の実験的なフィーチャをオンにしないといけない
304
-
305
- #![feature(fn_traits, unboxed_closures)]
306
-
307
-
308
-
309
- // 例2のクロージャの環境。countの&mut参照を持つ
310
-
311
- struct FuncEnv2<'a> {
312
-
313
- count: &'a mut i32,
314
-
315
- }
316
-
317
-
318
-
319
- // FnOnceの実装
320
-
321
- // FnMutを実装するためには、まずFnOnceを実装しないといけない
322
-
323
- impl<'a> FnOnce<()> for FuncEnv2<'a> {
324
-
325
- type Output = (); // クロージャの戻り値型
326
-
327
-
328
-
329
- // call_onceを実装する。このメソッドはselfを取る
330
-
331
- extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
332
-
333
- *self.count += 1;
334
-
335
- }
336
-
337
- }
338
-
339
-
340
-
341
- // FnMutの実装
342
-
343
- impl<'a> FnMut<()> for FuncEnv2<'a> {
344
-
345
- // call_mutを実装する。このメソッドは&mut selfを取る
346
-
347
- extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
348
-
349
- *self.count += 1;
350
-
351
- }
352
-
353
- }
354
-
355
-
356
-
357
- // この関数は引数にFnMutを実装するクロージャをとる
358
-
359
- fn take_fn_mut(func: &mut impl FnMut()) {
360
-
361
- func();
362
-
363
- }
364
-
365
-
366
-
367
- // この関数は引数にFnOnceを実装するクロージャをとる
368
-
369
- fn take_fn_once(func: impl FnOnce()) {
370
-
371
- func();
372
-
373
- }
374
-
375
-
376
-
377
- fn main() {
378
-
379
- let mut count = 10;
380
-
381
-
382
-
383
- // funcの実体は環境を表すstruct
384
-
385
- let mut func = FuncEnv2 {
386
-
387
- count: &mut count,
388
-
389
- };
390
-
391
-
392
-
393
- // FnMutを実装するのでcall_mut(&mut self, ...)を呼ぼうとする
394
-
395
- // &mut self(&mut funcと同じ)をするためにはfuncがmutでないとならない
396
-
397
- func();
398
-
399
-
400
-
401
- // これらも問題なく呼べる
402
-
403
- take_fn_mut(&mut func);
404
-
405
- take_fn_once(func);
406
-
407
- }
408
-
409
- ```
410
-
411
-
412
-
413
413
  `func()`としたとき、このクロージャは`FnMut`を実装するために、`call_mut(&mut self, ...)`メソッドを呼ぼうとします。`&mut self`は`&mut func`と同じですが、それをするためには、`func`が`mut`の必要があります。
414
414
 
415
415