Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

キャプチャ

クロージャは本質的に柔軟であり、アノテーションなしでクロージャを機能させるために必要なことを行います。これにより、キャプチャはユースケースに柔軟に適応でき、場合によってはムーブし、場合によっては借用します。 クロージャは変数をキャプチャできます。

  • 参照によって: &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_ はまだ利用可能であり、
    // 上の行のコメントを外してもエラーにはならない。
}

関連項目:

Boxstd::mem::drop