Rust 闭包

捕获方式、Fn/FnMut/FnOnce、move 关键字。

Intermediate
Key Points闭包按需要自动选择借用、可变借用或移动;`move` 强制移动。

基本语法

Rust
fn main() {
    let add = |a: i32, b: i32| a + b;
    println!("{}", add(1, 2));

    // 类型可以省略
    let double = |x| x * 2;
    println!("{}", double(5));

    // 多行需要花括号
    let calc = |x: i32| {
        let y = x * 2;
        y + 1
    };
    println!("{}", calc(3));
}
Output
3
10
7

捕获

Rust
fn main() {
    let name = String::from("Rust");

    // 不可变借用
    let greet = || println!("你好 {}", name);
    greet();
    greet();
    println!("{} 还能用", name);

    // 可变借用
    let mut count = 0;
    let mut inc = || {
        count += 1;
        println!("{}", count);
    };
    inc();
    inc();
}
Output
你好 Rust
你好 Rust
Rust 还能用
1
2

三种 Fn trait

TraitCapturesCan call
Fn&self(不可变借用)任意次
FnMut&mut self(可变借用)多次,但需可变引用
FnOnceself(移动)一次
Rust
fn call_fn<F: Fn()>(f: F) {
    f();
    f();
}

fn call_once<F: FnOnce()>(f: F) {
    f();
}

fn main() {
    call_fn(|| println!("Fn 可以多次调用"));

    let s = String::from("只配移动");
    call_once(move || println!("{}", s));
}
Output
Fn 可以多次调用
Fn 可以多次调用
只配移动

The trait follows the capture: read-only → Fn, mutate → FnMut, take ownership → FnOnce. They nest: an Fn closure also satisfies FnMut and FnOnce, not the reverse. So F: FnOnce() accepts every closure while F: Fn() accepts only non-mutating, non-moving ones.

move

Rust
use std::thread;

fn main() {
    let data = vec![1, 2, 3];

    // 不 move 会报错:闭包可能比 data 活得久
    let handle = thread::spawn(move || {
        println!("{:?}", data);
    });

    handle.join().unwrap();
}
Output
[1, 2, 3]
move captures all used variables by value; it does not move the closure. Threads require it because the compiler cannot prove a reference outlives the thread.

闭包作为返回值

Rust
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x + n
}

fn main() {
    let add5 = make_adder(5);
    println!("{}", add5(10));

    let add10 = make_adder(10);
    println!("{}", add10(10));
}
Output
15
20

Returning a closure needs impl Fn(...) (or Box<dyn Fn(...)>) because the closure's type is anonymous and unique. move is required here: n is local and dies when the function returns.

实用例子

Rust
fn main() {
    // 排序
    let mut v = vec![("b", 2), ("a", 3), ("c", 1)];
    v.sort_by(|x, y| x.1.cmp(&y.1));
    println!("{:?}", v);

    // 计数
    let mut counter = 0;
    let mut tick = || {
        counter += 1;
        counter
    };
    tick();
    tick();
    println!("{}", tick());
}
Output
[("c", 1), ("b", 2), ("a", 3)]
3

Friends & Partners