Skip to content

closures.rs

文件信息

  • 📄 原文件:02_closures.rs
  • 🔤 语言:rust

完整代码

rust
// ============================================================
//                      闭包(Closures)
// ============================================================
// 闭包是可以捕获所在环境变量的匿名函数
// 语法: |参数| 表达式  或  |参数| { 代码块 }
// 【与函数的区别】
//   1. 闭包可以捕获外部变量
//   2. 闭包参数和返回值类型可以推断(函数不行)
//   3. 闭包有三种 trait:Fn、FnMut、FnOnce

fn main() {
    println!("=== 闭包 ===");

    // ----------------------------------------------------------
    // 1. 闭包基本语法
    // ----------------------------------------------------------
    // 最简语法(单表达式,自动推断类型)
    let add_one = |x| x + 1;
    println!("add_one(5) = {}", add_one(5));

    // 带类型标注
    let add = |a: i32, b: i32| -> i32 { a + b };
    println!("add(3, 4) = {}", add(3, 4));

    // 多行闭包
    let calculate = |x: i32, y: i32| {
        let sum = x + y;
        let product = x * y;
        (sum, product)  // 返回元组
    };
    let (sum, product) = calculate(3, 4);
    println!("sum={}, product={}", sum, product);

    // 无参数闭包
    let say_hi = || println!("嗨!");
    say_hi();

    // ----------------------------------------------------------
    // 2. 捕获环境变量
    // ----------------------------------------------------------
    // 闭包会自动捕获外部变量,有三种捕获方式:
    // 1. 不可变借用(&T)  → 实现 Fn
    // 2. 可变借用(&mut T)→ 实现 FnMut
    // 3. 获取所有权(T)   → 实现 FnOnce

    // 不可变借用(默认优先选择)
    let name = String::from("Rust");
    let greet = || println!("你好, {}!", name);  // 借用 name
    greet();
    println!("name 仍然可用: {}", name);  // name 还在

    // 可变借用
    let mut count = 0;
    let mut increment = || {
        count += 1;  // 可变借用 count
        println!("计数: {}", count);
    };
    increment();
    increment();
    increment();
    // 【注意】increment 持有 count 的可变借用,
    //        在 increment 不再使用后才能再访问 count
    println!("最终计数: {}", count);

    // 获取所有权(使用 move 关键字)
    let data = vec![1, 2, 3];
    let print_data = move || {
        println!("data: {:?}", data);  // data 的所有权被移入闭包
    };
    print_data();
    // println!("{:?}", data);  // 错误!data 已被移入闭包

    // 【适用场景】move 在多线程中非常常用
    // 因为闭包需要在另一个线程中执行,必须拥有数据

    // ----------------------------------------------------------
    // 3. 闭包作为函数参数
    // ----------------------------------------------------------
    // 使用泛型 + trait bound 或 impl trait

    // 方式1: 泛型(推荐,可以内联优化)
    let result = apply_to_5(|x| x * 3);
    println!("apply_to_5(|x| x * 3) = {}", result);

    // 方式2: 作为 trait 对象(运行时多态,有额外开销)
    let ops: Vec<Box<dyn Fn(i32) -> i32>> = vec![
        Box::new(|x| x + 1),
        Box::new(|x| x * 2),
        Box::new(|x| x * x),
    ];
    for (i, op) in ops.iter().enumerate() {
        println!("ops[{}](5) = {}", i, op(5));
    }

    // ----------------------------------------------------------
    // 4. 闭包作为返回值
    // ----------------------------------------------------------
    let adder = make_adder(10);
    println!("make_adder(10)(5) = {}", adder(5));

    let multiplier = make_multiplier(3);
    println!("make_multiplier(3)(7) = {}", multiplier(7));

    // ----------------------------------------------------------
    // 5. Fn, FnMut, FnOnce 的区别
    // ----------------------------------------------------------
    // Fn:     可以多次调用,不修改捕获的变量(&self)
    // FnMut:  可以多次调用,可以修改捕获的变量(&mut self)
    // FnOnce: 只能调用一次,可以消耗捕获的变量(self)
    //
    // 继承关系: Fn : FnMut : FnOnce
    // 所有 Fn 都是 FnMut,所有 FnMut 都是 FnOnce

    // Fn 示例:不修改捕获的变量
    let x = 10;
    let fn_closure = || println!("Fn: x = {}", x);
    call_fn(&fn_closure);
    call_fn(&fn_closure);  // 可以多次调用

    // FnMut 示例:修改捕获的变量
    let mut total = 0;
    let mut fn_mut_closure = || {
        total += 1;
        println!("FnMut: total = {}", total);
    };
    call_fn_mut(&mut fn_mut_closure);
    call_fn_mut(&mut fn_mut_closure);

    // FnOnce 示例:消耗捕获的变量
    let data = String::from("一次性数据");
    let fn_once_closure = || {
        let _moved = data;  // 获取所有权
        println!("FnOnce: 数据已消耗");
    };
    call_fn_once(fn_once_closure);
    // call_fn_once(fn_once_closure);  // 错误!只能调用一次

    // ----------------------------------------------------------
    // 6. 实用示例
    // ----------------------------------------------------------
    println!("\n=== 实用示例 ===");

    // 过滤
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
    println!("偶数: {:?}", evens);

    // 映射
    let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
    println!("翻倍: {:?}", doubled);

    // 折叠(类似 reduce)
    let sum: i32 = numbers.iter().fold(0, |acc, &x| acc + x);
    println!("总和: {}", sum);

    // 排序(自定义比较)
    let mut words = vec!["banana", "apple", "cherry", "date"];
    words.sort_by(|a, b| a.len().cmp(&b.len()));  // 按长度排序
    println!("按长度排序: {:?}", words);

    // 组合迭代器操作(链式调用)
    let result: Vec<String> = (1..=5)
        .map(|x| x * x)
        .filter(|&x| x > 5)
        .map(|x| format!("{}²", (x as f64).sqrt() as i32))
        .collect();
    println!("链式操作: {:?}", result);

    println!("\n=== 闭包结束 ===");
}

// ----------------------------------------------------------
// 接受 Fn 闭包作为参数
// ----------------------------------------------------------
fn apply_to_5<F: Fn(i32) -> i32>(f: F) -> i32 {
    f(5)
}

// ----------------------------------------------------------
// 返回闭包(必须使用 impl Fn 或 Box<dyn Fn>)
// ----------------------------------------------------------
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
    move |y| x + y  // move 获取 x 的所有权
}

fn make_multiplier(factor: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |x| x * factor)
}

// ----------------------------------------------------------
// Fn / FnMut / FnOnce 参数示例
// ----------------------------------------------------------
fn call_fn(f: &dyn Fn()) {
    f();
}

fn call_fn_mut(f: &mut dyn FnMut()) {
    f();
}

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

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布