キャプチャ
クロージャは本質的に柔軟であり、アノテーションなしでクロージャを機能させるために必要なことを行います。これにより、キャプチャはユースケースに柔軟に適応でき、場合によってはムーブし、場合によっては借用します。 クロージャは変数をキャプチャできます。
- 参照によって:
&T - 可変参照によって:
&mut T - 値によって:
T
クロージャは変数を優先的に参照によってキャプチャし、必要な場合にのみ、より制約の強い方法へ移行します。
fn main() { use std::mem; let color = String::from("green"); // `color` を出力するクロージャ。これは即座に `color` を借用 (`&`) し、 // その借用とクロージャを `print` 変数に格納する。借用は // `print` が最後に使用されるまで継続する。 // // `println!` は引数を不変参照としてのみ必要とするため、 // それ以上に制約の強いものは課さない。 let print = || println!("`color`: {}", color); // 借用を使用してクロージャを呼び出す。 print(); // クロージャは `color` への不変参照だけを保持しているため、 // `color` は再び不変で借用できる。 let _reborrow = &color; print(); // `print` の最後の使用後は、ムーブまたは再借用が許可される let _color_moved = color; let mut count = 0; // `count` をインクリメントするクロージャは、`&mut count` または `count` の // どちらも受け取れるが、`&mut count` の方が制約が弱いため、それを受け取る。 // 即座に `count` を借用する。 // // 内部に `&mut` が格納されているため、`inc` には `mut` が必要。 // したがって、クロージャを呼び出すと `count` が変更されるため、`mut` が必要になる。 let mut inc = || { count += 1; println!("`count`: {}", count); }; // 可変借用を使用してクロージャを呼び出す。 inc(); // クロージャは後で呼び出されるため、まだ `count` を可変で借用している。 // 再借用しようとするとエラーになる。 // let _reborrow = &count; // ^ TODO: この行のコメントを外してみてください。 inc(); // クロージャはもはや `&mut count` を借用する必要がない。 // そのため、エラーなしで再借用できる let _count_reborrowed = &mut count; // 非コピー型。 let movable = Box::new(3); // `mem::drop` は `T` を必要とするため、これは値によって受け取る必要がある。 // コピー型であれば、元の値はそのまま残り、クロージャにコピーされる。 // 非コピー型はムーブする必要があるため、`movable` は即座に // クロージャへムーブする。 let consume = || { println!("`movable`: {:?}", movable); mem::drop(movable); }; // `consume` は変数を消費するため、これは一度しか呼び出せない。 consume(); // consume(); // ^ TODO: この行のコメントを外してみてください。 }
縦棒の前に move を使用すると、クロージャはキャプチャした変数の所有権を強制的に取得します。
fn main() { // `Vec` は非コピーのセマンティクスを持つ。 let haystack = vec![1, 2, 3]; let contains = move |needle| haystack.contains(needle); println!("{}", contains(&1)); println!("{}", contains(&4)); // println!("vec には {} 個の要素があります", haystack.len()); // ^ 上の行のコメントを外すと、コンパイル時エラーになる // 借用チェッカーは、ムーブされた後の変数の再利用を許可しないため。 // クロージャのシグネチャから `move` を削除すると、クロージャは // _haystack_ 変数を不変で借用するため、_haystack_ はまだ利用可能であり、 // 上の行のコメントを外してもエラーにはならない。 }