Rust 闭包

发布于:2025-07-06 ⋅ 阅读:(18) ⋅ 点赞:(0)

        在 Rust 中,闭包(closures)是强大的匿名函数,可以捕获其环境中的变量。以下是 Rust 闭包的全面解析:

一、基础闭包语法

// 无参数闭包
let greet = || println!("Hello!");
greet();

// 带参数闭包
let add = |a: i32, b: i32| -> i32 { a + b };
println!("Sum: {}", add(3, 5));

// 类型可省略(编译器推断)
let multiply = |x, y| x * y;
println!("Product: {}", multiply(4, 6));

二、捕获环境变量(核心特性)

1. 不可变借用(Fn trait)
let msg = "Hello".to_string();
let print_msg = || println!("{}", msg); // 捕获不可变引用
print_msg();
println!("Original: {}", msg); // 仍可访问
2. 可变借用(FnMut trait)
let mut count = 0;
let mut increment = || {
    count += 1; // 捕获可变引用
    println!("Count: {}", count);
};
increment();
increment();
3. 获取所有权(FnOnce trait)
let data = vec![1, 2, 3];
let consume_data = move || { // `move` 关键字强制获取所有权
    println!("Data: {:?}", data);
    // data 所有权被移动到闭包内
};
consume_data();
// println!("{:?}", data); // 错误!data 所有权已转移

三、闭包类型系统

特征 (Trait) 捕获方式 调用方法 可调用次数
Fn 不可变引用 call() 多次
FnMut 可变引用 call_mut() 多次
FnOnce 获取所有权 call_once() 仅一次

四、闭包作为函数参数

// 使用泛型和 trait 约束
fn apply_twice<F>(mut f: F, x: i32) -> i32
where
    F: FnMut(i32) -> i32,
{
    f(f(x))
}

let result = apply_twice(|x| x * 2, 5);
println!("Result: {}", result); // 20

五、闭包作为返回值

// 返回装箱闭包
fn make_adder(x: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |y| x + y)
}

let add_five = make_adder(5);
println!("10 + 5 = {}", add_five(10));

// 使用 impl Trait(更高效)
fn multiplier(factor: i32) -> impl Fn(i32) -> i32 {
    move |x| x * factor
}
let triple = multiplier(3);
println!("7 * 3 = {}", triple(7));

六、闭包与迭代器(黄金组合)

let numbers = vec![1, 2, 3, 4, 5];

// 过滤偶数
let evens: Vec<_> = numbers.iter()
    .filter(|&x| x % 2 == 0)
    .collect();

// 平方和
let sum_of_squares: i32 = numbers.iter()
    .map(|x| x * x)
    .sum();

七、高级用法

1. 环境变量选择性捕获
let (a, mut b) = (10, 20);
let closure = || {
    println!("a: {}", a);  // 不可变借用 a
    b += 1;               // 可变借用 b
};
closure();
2. 结构体中存储闭包
struct Cache<T>
where
    T: Fn(i32) -> i32,
{
    calculation: T,
    value: Option<i32>,
}

let expensive_closure = |x| {
    // 模拟复杂计算
    x * x
};
let mut c = Cache {
    calculation: expensive_closure,
    value: None,
};
3. 多线程中的闭包
use std::thread;

let value = 42;
let handle = thread::spawn(move || {
    println!("Value in thread: {}", value);
});
handle.join().unwrap();

八、性能特点

  1. 零成本抽象:闭包在运行时通常被优化为普通函数

  2. 内联优化:小闭包常被编译器内联

  3. 无堆分配:不捕获环境的闭包无需堆分配

  4. 类型大小:捕获的变量决定闭包大小

九、闭包 vs 函数指针

特性 闭包 函数指针 (fn)
捕获环境 支持 不支持
大小 取决于捕获内容 固定大小(指针)
性能 通常更高 间接调用开销
使用场景 需要上下文时 C 交互/无状态操作

十、最佳实践

  1. 优先使用不可变借用(Fn

  2. 需要修改环境时用 FnMut

  3. 跨线程传递数据用 move + FnOnce

  4. 简单逻辑直接用闭包而非定义函数

  5. 复杂逻辑考虑重构为命名函数

// 实用示例:自定义排序
let mut words = ["apple", "banana", "cherry"];
words.sort_by(|a, b| a.len().cmp(&b.len()));
println!("Sorted: {:?}", words); // ["apple", "cherry", "banana"]

        闭包是 Rust 函数式编程的核心,合理使用可使代码更简洁高效,同时保持内存安全性。