要素の捕捉
クロージャはとてもフレキシブルに動作するように出来ています。クロージャにおいて型アノテーションをする必要が無いのは前述の仕組みのためですが、この仕組みのおかげでユースケースに応じて参照を取得したり値そのものを取得したりといった動作が可能になります。クロージャは外側の環境にある要素を、以下の形で取得することができます。
- 参照:
&T - ミュータブルな参照:
&mut T - 値そのもの:
T
クロージャは出来る限り参照を取得しようとし、その他2つは必要なときのみ取得します。
fn main() {
use std::mem;
let color = String::from("green");
// A closure to print `color` which immediately borrows (`&`) `color` and
// stores the borrow and closure in the `print` variable. It will remain
// borrowed until `print` is used the last time.
//
// `println!` only requires arguments by immutable reference so it doesn't
// impose anything more restrictive.
let print = || println!("`color`: {}", color);
// 借用を行ったクロージャを呼び出します。
print();
// `color` can be borrowed immutably again, because the closure only holds
// an immutable reference to `color`.
let _reborrow = &color;
print();
// 最後に`print`を使用した後は移動や再借用が許可されます。
let _color_moved = color;
let mut count = 0;
// `count`をインクリメントするためのクロージャ。`count`と`&mut count`
// の両方を取ることができますが、後者のほうが制限が少ないため、
// (訳注: `count`だと`&mut count`と違い、一度しか呼ぶことができない。)
// そちらを取ります。直後に`count`を借用します。
//
// `inc`には`mut`をつける必要があります。なぜならミュータブルな型が
// 中で使用されているからです。ミュータブルなクロージャは呼ぶたびに
// 内部変数を変更します。
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
// ミュータブルな借用を使ってクロージャを実行。
inc();
// The closure still mutably borrows `count` because it is called later.
// An attempt to reborrow will lead to an error.
// let _reborrow = &count;
// ^ TODO: try uncommenting this line.
inc();
// クロージャはもう`&mut count`を借用する必要がありません。
// なので、エラーを起こさず再借用することができます。
let _count_reborrowed = &mut count;
// コピー不可能な型
let movable = Box::new(3);
// `mem::drop`は`T`(ジェネリック型)を取る必要があるため、このクロージャは
// 参照ではなく値を取ります。その場合、もしもコピー可能な値ならば、
// 元の値はそのままでコピーのみを取ります。
// 不可能ならば値そのものを移動させます。
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!("There're {} elements in vec", haystack.len());
// ^ 上の行のコメントアウトを解除すると、コンパイル時エラーになります。
// これは変数の所有権が移った後の再利用を借用チェッカーが許可しないからです。
// クロージャのシグネチャから`move`を削除すると、クロージャは
// _haystack_ 変数をイミュータブルで借用するようになります。
// そのため _haystack_ はまだ利用可能となり、上の行のコメントアウトを
// 解除してもエラーが発生しなくなります。
}