捕获 - Rust By Example
Description: Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.

## 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`。
        //
        // `inc` 需要 `mut` 因为内部存储了 `&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_ 仍可用，
        // 取消注释上面的行不会导致错误。
    }

### 另请参阅：

`Box` 和 `std::mem::drop`