Skip to content

Rust 闭包(Closure)完全指南

📚 一、闭包基本概念

1. 什么是闭包?

闭包是可以捕获其环境的匿名函数。它们可以存储为变量、作为参数传递,并在其他地方被调用。

2. 与普通函数的区别

// 普通函数
fn add(x: i32, y: i32) -> i32 {
    x + y
}

// 闭包
let add_closure = |x: i32, y: i32| -> i32 { x + y };
let add_closure_inferred = |x, y| x + y;  // 类型推断

🎯 二、闭包语法

1. 基本语法

// 1. 完整语法
let closure1 = |x: i32, y: i32| -> i32 {
    x + y
};

// 2. 类型推断
let closure2 = |x, y| x + y;

// 3. 无参数
let closure3 = || println!("Hello!");

// 4. 单个参数可省略括号
let closure4 = |x| x * 2;

// 5. 多行闭包
let closure5 = |x: i32| {
    let y = x * 2;
    y + 1
};

2. 调用闭包

fn main() {
    // 定义闭包
    let add = |x, y| x + y;
    let greet = || println!("Hello, World!");
    let square = |x: i32| -> i32 { x * x };

    // 调用闭包
    let result = add(5, 3);        // 8
    greet();                       // Hello, World!
    let squared = square(4);       // 16

    println!("add(5, 3) = {}", result);
    println!("square(4) = {}", squared);

    // 闭包可以重复调用
    println!("add(10, 20) = {}", add(10, 20));  // 30
}

🔧 三、闭包的三种特质

Rust 闭包实现了以下三个 trait 之一(或全部):

  1. FnOnce - 可以调用一次,获取所有权
  2. FnMut - 可以多次调用,可变借用
  3. Fn - 可以多次调用,不可变借用
fn closure_traits() {
    // 1. Fn - 不可变借用(最常见)
    let x = 10;
    let print_x = || println!("x = {}", x);  // 捕获 x 的不可变引用
    print_x();  // 可以多次调用
    print_x();

    // 2. FnMut - 可变借用
    let mut counter = 0;
    let mut increment = || {
        counter += 1;
        println!("Counter: {}", counter);
    };
    increment();  // Counter: 1
    increment();  // Counter: 2

    // 3. FnOnce - 获取所有权
    let s = String::from("hello");
    let consume_string = || {
        println!("Consuming: {}", s);
        drop(s);  // 消耗所有权
    };
    consume_string();  // 只能调用一次
    // consume_string();  // ❌ 编译错误:s 已被移动
}

📦 四、闭包捕获环境

1. 捕获方式

fn capture_modes() {
    let x = 5;
    let y = 10;
    let mut z = 15;
    let w = String::from("hello");

    // 1. 不可变借用捕获
    let print_sum = || println!("x + y = {}", x + y);
    print_sum();
    println!("x is still available: {}", x);  // ✅

    // 2. 可变借用捕获
    let mut increment_z = || {
        z += 1;
        println!("z = {}", z);
    };
    increment_z();  // z = 16
    // println!("z: {}", z);  // ❌ 编译错误:z 被可变借用

    // 3. 所有权捕获
    let consume_w = move || {
        println!("w = {}", w);
        // w 的所有权被移动到闭包内
    };
    consume_w();
    // println!("w: {}", w);  // ❌ 编译错误:w 已被移动
}

2. move 关键字

fn move_keyword() {
    // 示例1: 在多线程中使用
    let data = vec![1, 2, 3, 4, 5];

    // 使用 move 强制获取所有权
    let closure = move || {
        println!("Data: {:?}", data);
    };

    closure();
    // println!("{:?}", data);  // ❌ 错误:data 已被移动

    // 示例2: 闭包传递给线程
    use std::thread;

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

    thread::spawn(move || {
        println!("Numbers in thread: {:?}", numbers);
    }).join().unwrap();

    // 示例3: 捕获多个变量
    let s1 = String::from("hello");
    let s2 = String::from("world");
    let s3 = String::from("rust");

    let closure = move || {
        println!("{} {} {}", s1, s2, s3);
    };

    closure();
}

🔄 五、闭包作为参数

1. 函数参数

// 接受闭包作为参数
fn apply<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,  // F 实现了 Fn(i32) -> i32
{
    f(x)
}

// 更通用的写法
fn apply_generic<F, T, R>(f: F, value: T) -> R
where
    F: Fn(T) -> R,
    T: Clone,  // 如果需要多次使用
{
    f(value.clone())
}

fn main() {
    let double = |x| x * 2;
    let square = |x| x * x;

    println!("apply(double, 5) = {}", apply(double, 5));    // 10
    println!("apply(square, 5) = {}", apply(square, 5));    // 25

    // 内联闭包
    println!("apply(|x| x + 1, 5) = {}", apply(|x| x + 1, 5));  // 6
}

2. 返回闭包

// 返回闭包
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
    move |x| x * factor
}

// 返回 Box<dyn Fn> 处理不同类型
fn make_operation(op: &str) -> Box<dyn Fn(i32, i32) -> i32> {
    match op {
        "add" => Box::new(|x, y| x + y),
        "sub" => Box::new(|x, y| x - y),
        "mul" => Box::new(|x, y| x * y),
        _ => Box::new(|x, y| x / y),
    }
}

fn main() {
    let double = make_multiplier(2);
    let triple = make_multiplier(3);

    println!("double(5) = {}", double(5));  // 10
    println!("triple(5) = {}", triple(5));  // 15

    let add = make_operation("add");
    let mul = make_operation("mul");

    println!("add(5, 3) = {}", add(5, 3));  // 8
    println!("mul(5, 3) = {}", mul(5, 3));  // 15
}

🎨 六、闭包与迭代器

1. 常见迭代器方法

fn closures_with_iterators() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // 1. map - 转换
    let doubled: Vec<_> = numbers.iter()
        .map(|x| x * 2)
        .collect();

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

    // 3. fold - 累积
    let sum = numbers.iter()
        .fold(0, |acc, &x| acc + x);

    // 4. for_each - 遍历
    numbers.iter()
        .for_each(|x| println!("Number: {}", x));

    // 5. 链式调用
    let result: i32 = numbers.iter()
        .filter(|&x| x % 2 == 0)    // 偶数
        .map(|&x| x * 3)            // 乘以3
        .filter(|&x| x > 10)        // 大于10
        .sum();                     // 求和

    println!("Result: {}", result);
}

2. 复杂闭包示例

struct Person {
    name: String,
    age: u8,
    salary: f64,
}

fn complex_closure_example() {
    let people = vec![
        Person { name: "Alice".to_string(), age: 25, salary: 50000.0 },
        Person { name: "Bob".to_string(), age: 30, salary: 60000.0 },
        Person { name: "Charlie".to_string(), age: 35, salary: 70000.0 },
        Person { name: "Diana".to_string(), age: 28, salary: 55000.0 },
    ];

    // 复杂的数据处理管道
    let total_bonus: f64 = people.iter()
        .filter(|p| p.age >= 30)                    // 30岁以上
        .map(|p| (p.salary * 0.1))                  // 10%奖金
        .filter(|&bonus| bonus > 5000.0)           // 奖金超过5000
        .fold(0.0, |acc, bonus| acc + bonus);      // 总和

    println!("Total bonus: ${:.2}", total_bonus);

    // 使用闭包分组
    use std::collections::HashMap;

    let age_groups: HashMap<u8, Vec<&Person>> = people.iter()
        .fold(HashMap::new(), |mut map, person| {
            let age_group = (person.age / 10) * 10;  // 按10岁分组
            map.entry(age_group).or_insert_with(Vec::new).push(person);
            map
        });

    for (age_group, group) in age_groups {
        println!("Age group {}: {} people", age_group, group.len());
    }
}

🏗️ 七、闭包在结构体中的使用

1. 存储闭包

struct Cache<T, R>
where
    T: Fn() -> R + Clone,
    R: Clone,
{
    calculation: T,
    value: Option<R>,
}

impl<T, R> Cache<T, R>
where
    T: Fn() -> R + Clone,
    R: Clone,
{
    fn new(calculation: T) -> Self {
        Cache {
            calculation,
            value: None,
        }
    }

    fn value(&mut self) -> R {
        match &self.value {
            Some(v) => v.clone(),
            None => {
                let v = (self.calculation)();
                self.value = Some(v.clone());
                v
            }
        }
    }
}

fn main() {
    let mut cache = Cache::new(|| {
        println!("Calculating expensive value...");
        42
    });

    println!("First call: {}", cache.value());  // 会计算
    println!("Second call: {}", cache.value()); // 从缓存获取
}

2. 回调系统

struct EventHandler<F>
where
    F: Fn(&str),
{
    callbacks: Vec<F>,
}

impl<F> EventHandler<F>
where
    F: Fn(&str),
{
    fn new() -> Self {
        EventHandler { callbacks: Vec::new() }
    }

    fn register(&mut self, callback: F) {
        self.callbacks.push(callback);
    }

    fn trigger(&self, event: &str) {
        for callback in &self.callbacks {
            callback(event);
        }
    }
}

fn main() {
    let mut handler = EventHandler::new();

    // 注册多个闭包作为回调
    handler.register(|event| println!("Callback 1 received: {}", event));
    handler.register(|event| println!("Callback 2 says: Event '{}' occurred", event));

    // 触发事件
    handler.trigger("click");
    handler.trigger("hover");
}

⚡ 八、性能优化技巧

1. 避免闭包开销

use std::time::Instant;

fn performance_comparison() {
    let data: Vec<i32> = (0..1_000_000).collect();

    // 方法1: 内联闭包
    let start = Instant::now();
    let sum1: i32 = data.iter()
        .map(|&x| x * 2)
        .sum();
    let duration1 = start.elapsed();

    // 方法2: 预定义闭包
    let double = |x: i32| x * 2;
    let start = Instant::now();
    let sum2: i32 = data.iter()
        .map(|&x| double(x))
        .sum();
    let duration2 = start.elapsed();

    // 方法3: 函数
    fn double_fn(x: i32) -> i32 { x * 2 }
    let start = Instant::now();
    let sum3: i32 = data.iter()
        .map(|&x| double_fn(x))
        .sum();
    let duration3 = start.elapsed();

    println!("Inline closure: {} in {:?}", sum1, duration1);
    println!("Predefined closure: {} in {:?}", sum2, duration2);
    println!("Function: {} in {:?}", sum3, duration3);
}

2. 闭包大小优化

fn closure_size_optimization() {
    use std::mem;

    // 小闭包
    let small_closure = |x: i32| x + 1;
    println!("Small closure size: {} bytes", mem::size_of_val(&small_closure));

    // 捕获环境的闭包
    let data = vec![1, 2, 3];
    let medium_closure = || {
        println!("Data: {:?}", data);
    };
    println!("Medium closure size: {} bytes", mem::size_of_val(&medium_closure));

    // 大闭包
    let large_data: Vec<i32> = (0..1000).collect();
    let large_closure = move || {
        println!("Large data length: {}", large_data.len());
    };
    println!("Large closure size: {} bytes", mem::size_of_val(&large_closure));
}

🔧 九、实用工具函数

1. 闭包工厂

fn create_calculator(operation: char) -> impl Fn(f64, f64) -> f64 {
    match operation {
        '+' => |x, y| x + y,
        '-' => |x, y| x - y,
        '*' => |x, y| x * y,
        '/' => |x, y| {
            if y == 0.0 {
                f64::NAN
            } else {
                x / y
            }
        },
        '^' => |x, y| x.powf(y),
        _ => |_, _| 0.0,
    }
}

fn main() {
    let add = create_calculator('+');
    let mul = create_calculator('*');
    let pow = create_calculator('^');

    println!("5 + 3 = {}", add(5.0, 3.0));    // 8.0
    println!("5 * 3 = {}", mul(5.0, 3.0));    // 15.0
    println!("5 ^ 3 = {}", pow(5.0, 3.0));    // 125.0
}

2. 记忆化(Memoization)

use std::collections::HashMap;
use std::hash::Hash;
use std::sync::{Arc, Mutex};

struct Memo<F, A, R>
where
    F: Fn(A) -> R,
    A: Eq + Hash + Clone,
    R: Clone,
{
    calculation: Arc<F>,
    cache: Mutex<HashMap<A, R>>,
}

impl<F, A, R> Memo<F, A, R>
where
    F: Fn(A) -> R,
    A: Eq + Hash + Clone,
    R: Clone,
{
    fn new(calculation: F) -> Self {
        Memo {
            calculation: Arc::new(calculation),
            cache: Mutex::new(HashMap::new()),
        }
    }

    fn compute(&self, arg: A) -> R {
        {
            let cache = self.cache.lock().unwrap();
            if let Some(result) = cache.get(&arg) {
                return result.clone();
            }
        }

        let result = (self.calculation)(arg.clone());

        let mut cache = self.cache.lock().unwrap();
        cache.insert(arg, result.clone());

        result
    }
}

fn main() {
    let expensive_closure = |x: i32| -> i32 {
        println!("Computing for {}...", x);
        x * x
    };

    let memo = Memo::new(expensive_closure);

    println!("First call: {}", memo.compute(5));  // 会计算
    println!("Second call: {}", memo.compute(5)); // 从缓存获取
    println!("Third call: {}", memo.compute(10)); // 会计算
    println!("Fourth call: {}", memo.compute(10)); // 从缓存获取
}

🧪 十、测试闭包

1. 单元测试

#[cfg(test)]
mod tests {
    #[test]
    fn test_basic_closure() {
        let add = |x, y| x + y;
        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-1, 1), 0);
    }

    #[test]
    fn test_closure_capture() {
        let x = 10;
        let get_x = || x;
        assert_eq!(get_x(), 10);
    }

    #[test]
    fn test_move_closure() {
        let s = String::from("hello");
        let closure = move || s.len();
        assert_eq!(closure(), 5);
    }

    #[test]
    fn test_closure_in_struct() {
        struct Processor<F>
        where
            F: Fn(i32) -> i32,
        {
            process: F,
        }

        let processor = Processor { process: |x| x * 2 };
        assert_eq!((processor.process)(5), 10);
    }
}

2. 集成测试

fn process_numbers<F>(numbers: &[i32], processor: F) -> Vec<i32>
where
    F: Fn(i32) -> i32,
{
    numbers.iter().map(|&x| processor(x)).collect()
}

#[cfg(test)]
mod integration_tests {
    use super::*;

    #[test]
    fn test_process_numbers() {
        let numbers = vec![1, 2, 3, 4, 5];

        // 测试平方
        let squared = process_numbers(&numbers, |x| x * x);
        assert_eq!(squared, vec![1, 4, 9, 16, 25]);

        // 测试加一
        let incremented = process_numbers(&numbers, |x| x + 1);
        assert_eq!(incremented, vec![2, 3, 4, 5, 6]);
    }
}

🎯 十一、最佳实践

1. 何时使用闭包

// ✅ 适合使用闭包的场景
fn good_use_cases() {
    // 1. 回调函数
    let callback = |result| println!("Result: {}", result);

    // 2. 事件处理
    let click_handler = |x, y| println!("Clicked at ({}, {})", x, y);

    // 3. 配置行为
    let logger = |level: &str, message: &str| {
        println!("[{}] {}", level, message);
    };

    // 4. 简单的转换逻辑
    let data = vec![1, 2, 3];
    let processed: Vec<_> = data.iter()
        .map(|x| x * 2)
        .filter(|x| *x > 3)
        .collect();

    // 5. 线程任务
    use std::thread;
    thread::spawn(|| {
        println!("Running in thread");
    });
}

// ❌ 不适合使用闭包的场景
fn bad_use_cases() {
    // 1. 复杂的业务逻辑 - 应该用函数
    // ❌ 不好的做法:
    let complex_logic = |x: i32| -> i32 {
        if x < 0 {
            -1
        } else if x == 0 {
            0
        } else if x < 10 {
            x * 2
        } else {
            x + 10
        }
    };

    // ✅ 好的做法:
    fn complex_logic_fn(x: i32) -> i32 {
        if x < 0 {
            -1
        } else if x == 0 {
            0
        } else if x < 10 {
            x * 2
        } else {
            x + 10
        }
    }

    // 2. 需要递归的情况
    // ❌ 闭包递归很复杂
    // let factorial = |n: u32| -> u32 {
    //     if n <= 1 { 1 } else { n * factorial(n - 1) }  // ❌ 不能直接递归
    // };

    // ✅ 用函数
    fn factorial(n: u32) -> u32 {
        if n <= 1 { 1 } else { n * factorial(n - 1) }
    }
}

2. 性能建议

fn performance_recommendations() {
    // 1. 避免在热路径中创建闭包
    let data = vec![1, 2, 3, 4, 5];

    // ❌ 不好的做法:在循环中创建闭包
    for _ in 0..1000 {
        let processor = |x| x * 2;  // 每次循环都创建新闭包
        // 使用 processor
    }

    // ✅ 好的做法:提前创建闭包
    let processor = |x| x * 2;
    for _ in 0..1000 {
        // 使用 processor
    }

    // 2. 考虑闭包大小
    let large_data = vec![0u8; 1024];  // 1KB
    let small_closure = || large_data.len();  // 闭包会包含 large_data

    println!("Closure size: {} bytes", std::mem::size_of_val(&small_closure));

    // 3. 使用引用而非 move
    let config = vec![1, 2, 3];

    // ❌ 不必要的 move
    let closure1 = move || config.len();

    // ✅ 使用引用
    let closure2 = || config.len();  // 自动捕获引用
}

📦 十二、完整示例:配置系统

use std::collections::HashMap;
use std::sync::{Arc, Mutex};

type Callback<T> = Box<dyn Fn(&T) + Send + Sync>;

struct EventSystem<T> {
    callbacks: Arc<Mutex<HashMap<String, Vec<Callback<T>>>>>,
}

impl<T> EventSystem<T>
where
    T: Clone + Send + Sync + 'static,
{
    fn new() -> Self {
        EventSystem {
            callbacks: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    fn subscribe<F>(&self, event: &str, callback: F)
    where
        F: Fn(&T) + Send + Sync + 'static,
    {
        let mut callbacks = self.callbacks.lock().unwrap();
        callbacks
            .entry(event.to_string())
            .or_insert_with(Vec::new)
            .push(Box::new(callback));
    }

    fn emit(&self, event: &str, data: T) {
        if let Some(callbacks) = self.callbacks.lock().unwrap().get(event) {
            for callback in callbacks {
                callback(&data);
            }
        }
    }
}

fn main() {
    // 创建事件系统
    let system = EventSystem::new();

    // 订阅事件
    system.subscribe("user.login", |user: &String| {
        println!("User logged in: {}", user);
    });

    system.subscribe("user.logout", |user: &String| {
        println!("User logged out: {}", user);
    });

    system.subscribe("user.login", |user: &String| {
        println!("[AUDIT] Login event for: {}", user);
    });

    // 触发事件
    system.emit("user.login", "alice".to_string());
    system.emit("user.logout", "bob".to_string());
    system.emit("user.login", "charlie".to_string());

    // 输出:
    // User logged in: alice
    // [AUDIT] Login event for: alice
    // User logged out: bob
    // User logged in: charlie
    // [AUDIT] Login event for: charlie
}

🔄 十三、闭包类型注解

1. 何时需要类型注解

fn when_to_annotate() {
    // 情况1: 编译器无法推断类型
    // let closure = |x| x;  // ❌ 错误:无法推断类型
    let closure = |x: i32| x;  // ✅ 明确指定

    // 情况2: 返回闭包
    fn returns_closure() -> impl Fn(i32) -> i32 {
        |x| x * 2
    }

    // 情况3: 存储为 trait 对象
    let closures: Vec<Box<dyn Fn(i32) -> i32>> = vec![
        Box::new(|x: i32| x + 1),
        Box::new(|x: i32| x * 2),
    ];

    // 情况4: 作为结构体字段
    struct Processor<F>
    where
        F: Fn(i32) -> i32,
    {
        processor: F,
    }

    let p = Processor { processor: |x: i32| x + 1 };
}

2. 类型别名

type StringProcessor = Box<dyn Fn(&str) -> String>;

struct Pipeline {
    processors: Vec<StringProcessor>,
}

impl Pipeline {
    fn new() -> Self {
        Pipeline { processors: Vec::new() }
    }

    fn add_processor<F>(&mut self, processor: F)
    where
        F: Fn(&str) -> String + 'static,
    {
        self.processors.push(Box::new(processor));
    }

    fn process(&self, input: &str) -> String {
        let mut result = input.to_string();
        for processor in &self.processors {
            result = processor(&result);
        }
        result
    }
}

fn main() {
    let mut pipeline = Pipeline::new();

    pipeline.add_processor(|s| s.to_uppercase());
    pipeline.add_processor(|s| s.replace(" ", "_"));
    pipeline.add_processor(|s| format!("[{}]", s));

    let result = pipeline.process("hello world");
    println!("Result: {}", result);  // [HELLO_WORLD]
}

🎯 总结要点

  1. 闭包是匿名函数,可以捕获环境变量
  2. 三种 traitFnOnceFnMutFn
  3. move 关键字 强制获取所有权
  4. 类型推断 通常不需要显式注解
  5. 性能良好 通常与函数调用开销相同
  6. 灵活使用 作为参数、返回值、存储在结构体中
  7. 注意生命周期 特别是当闭包跨越作用域时

闭包是 Rust 中强大而灵活的特性,使得函数式编程风格和回调模式在 Rust 中变得非常自然。正确使用闭包可以编写出更简洁、更表达性的代码。