Rust 学习之闭包

Rust 中的闭包(Closure)是一种可以捕获周围环境变量的匿名函数,它的核心特点是:简洁、灵活,能 “记住” 定义时的上下文变量。如果用生活场景比喻,闭包就像 “临时雇佣的助手”—— 不需要提前正式 “注册”(定义函数名),能快速接手一些短期任务,还能顺手用用身边的工具(捕获环境变量)。

Rust 中的闭包(Closure)是一种可以捕获周围环境变量的匿名函数,它的核心特点是:简洁、灵活,能 “记住” 定义时的上下文变量。如果用生活场景比喻,闭包就像 “临时雇佣的助手”—— 不需要提前正式 “注册”(定义函数名),能快速接手一些短期任务,还能顺手用用身边的工具(捕获环境变量)。

闭包的核心使用场景及形象比喻

1. 作为 “短期任务” 传递给函数(最常见场景)

场景:当你需要给一个函数传递一段 “临时逻辑”(比如处理数据、过滤条件),且这段逻辑很短、不需要复用,用闭包比单独定义函数更简洁。

比喻:就像你请快递员(函数)帮忙送包裹时,临时告诉他 “送到后请放在门口鞋柜上”(闭包)—— 这段指令很短,没必要提前写在 “快递规则手册”(定义普通函数)里。

代码示例:迭代器处理数据

fn main() {
    let numbers = vec![1, 2, 3, 4];
    
    // 用闭包作为map的参数,定义“将每个元素乘2”的临时逻辑
    let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
    
    println!("{:?}", doubled); // 输出:[2, 4, 6, 8]
}
  • 这里的 |x| x * 2 就是闭包,作为 map 方法的参数,临时定义了数据转换逻辑。如果用普通函数,需要单独定义 fn double(x: &i32) -> i32 { x * 2 },反而显得冗余。

2. 捕获环境变量(“带工具干活”)

场景:闭包可以访问定义它时所在作用域的变量(无需通过参数传递),适合需要 “结合外部数据” 的逻辑。

比喻:就像你让助手(闭包)帮忙整理文件时,他会自动用你桌上的剪刀和胶带(环境变量)—— 不用你特意把工具递给他,他 “记得” 工具在哪。

代码示例:捕获外部变量进行计算

fn main() {
    let factor = 10; // 环境变量
    
    // 闭包捕获factor,无需通过参数传递
    let multiply = |x: i32| x * factor;
    
    println!("{}", multiply(5)); // 输出:50(5 * 10)
}
  • 闭包 |x: i32| x * factor 直接使用了外部的 factor 变量,而普通函数无法做到这一点(必须通过参数传入)。

3. 延迟执行逻辑(“先记下来,稍后再做”)

场景:需要 “先定义一段逻辑,在未来某个时机执行”(比如回调函数、延迟计算)。

比喻:就像你写了一张便签(闭包)放在桌上,上面写着 “下午 3 点浇花”,到时间再拿起来执行 —— 逻辑先定义,执行时机灵活。

代码示例:延迟计算一个值

fn main() {
    let x = 10;
    let y = 20;
    
    // 定义闭包:先“记住”x和y,不立即计算
    let calculate = || x + y;
    
    // 稍后执行闭包
    println!("结果:{}", calculate()); // 输出:30
}
  • 闭包 calculate 定义时并没有计算 x + y,而是在调用 calculate() 时才执行,实现了逻辑的延迟执行。

4. 简化状态依赖的逻辑(“带状态的工具”)

场景:当逻辑需要依赖某个 “状态”(且状态会变化)时,闭包可以捕获并更新这个状态,避免用结构体手动管理。

比喻:就像一个带计数器的工具(闭包),每次使用都会自动加 1—— 你不用自己记次数,工具会 “记住” 当前状态。

代码示例:用闭包维护一个计数器

fn main() {
    let mut count = 0;
    
    // 闭包捕获mut count,每次调用都让它加1
    let mut increment = || {
        count += 1;
        count
    };
    
    println!("{}", increment()); // 输出:1
    println!("{}", increment()); // 输出:2(闭包“记住”了count的最新值)
}
  • 闭包 increment 捕获了可变变量 count,每次调用都会更新它,相当于一个 “带状态的简易函数”。如果用普通函数,需要用结构体存储 count,代码会更繁琐。

5. 作为返回值(“打包一段逻辑带走”)

场景:函数可以返回一个闭包,让调用者获得一段 “预配置” 的逻辑(可能包含函数内部的变量)。

比喻:就像餐厅服务员(函数)给你一个 “预制调料包”(闭包),里面已经按比例配好了盐和糖(函数内部的变量),你拿回去直接用就行。

代码示例:返回一个带预配置参数的闭包

// 返回一个闭包:该闭包会用base作为基础值进行计算
fn make_calculator(base: i32) -> impl Fn(i32) -> i32 {
    // 闭包捕获base,返回给调用者
    move |x| x + base
}

fn main() {
    let add_5 = make_calculator(5); // 获得“加5”的闭包
    let add_10 = make_calculator(10); // 获得“加10”的闭包
    
    println!("{}", add_5(3)); // 输出:8(3 + 5)
    println!("{}", add_10(3)); // 输出:13(3 + 10)
}
  • 函数 make_calculator 返回的闭包携带了参数 base,调用者拿到后可以直接使用这段预配置的逻辑。

闭包 vs 普通函数:什么时候用闭包?

特性

闭包(Closure)

普通函数(Function)

匿名性

匿名(无需命名)

必须命名

环境捕获

可以捕获周围变量

不能捕获环境变量(只能通过参数传递)

语法简洁性

语法简短(`

参数

表达式 `)

语法固定(fn 名称(参数) -> 返回值 { ... }

复用性

适合短期、一次性逻辑

适合长期复用、逻辑复杂的场景


简单说:短期、简单、需要访问环境变量的逻辑,用闭包;长期复用、逻辑复杂的逻辑,用普通函数

总结:闭包的核心形象

闭包就像 “灵活的临时助手”:

  • 不用提前 “登记姓名”(匿名),随用随写;

  • 能顺手用用身边的工具(捕获环境变量);

  • 适合干短期、简单的活(作为参数、延迟执行);

  • 干完就走,不占地方(减少代码冗余)。

这也是为什么闭包在 Rust 的迭代器、异步编程、回调逻辑中被广泛使用 —— 它让代码更简洁、更灵活。

LICENSED UNDER CC BY-NC-SA 4.0
评论