跳到主要内容

闭包基础(Closures Basics)

闭包(Closures)是 Rust 中的匿名函数,可以捕获其定义环境中的变量。闭包在函数式编程、迭代器、异步编程等场景中广泛使用。

什么是闭包?

闭包是一个可以:

  • 捕获环境变量的匿名函数
  • 存储在变量中或作为参数传递
  • 在定义位置之外调用

基本语法

闭包定义

fn main() {
// 基本闭包语法
let add = |x, y| x + y;
println!("2 + 3 = {}", add(2, 3));

// 带类型注解的闭包
let multiply: fn(i32, i32) -> i32 = |x, y| x * y;
println!("4 * 5 = {}", multiply(4, 5));

// 多行闭包
let complex_operation = |x: i32| {
let doubled = x * 2;
let squared = doubled * doubled;
squared + 1
};
println!("复杂操作结果: {}", complex_operation(3));
}

闭包 vs 函数

fn main() {
// 函数定义
fn add_function(x: i32, y: i32) -> i32 {
x + y
}

// 闭包定义
let add_closure = |x, y| x + y;

// 使用方式相同
println!("函数: {}", add_function(2, 3));
println!("闭包: {}", add_closure(2, 3));
}

捕获环境变量

闭包可以捕获其定义环境中的变量:

不可变借用

fn main() {
let x = 4;

// 闭包捕获 x 的不可变引用
let equal_to_x = |z| z == x;

let y = 4;
assert!(equal_to_x(y));

// x 仍然可以使用
println!("x = {}", x);
}

可变借用

fn main() {
let mut x = 4;

// 闭包捕获 x 的可变引用
let mut increment_x = || {
x += 1;
x
};

println!("递增后: {}", increment_x());
println!("再次递增: {}", increment_x());

// 注意:在闭包存在期间,x 不能被其他方式使用
}

获取所有权

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

// 使用 move 关键字获取所有权
let consume_x = move || {
println!("向量: {:?}", x);
x.len()
};

println!("长度: {}", consume_x());

// x 已经被移动,不能再使用
// println!("{:?}", x); // 编译错误!
}

闭包类型推断

Rust 可以推断闭包的参数和返回值类型:

fn main() {
// 类型推断
let add = |x, y| x + y;

// 第一次调用确定类型
let result = add(5, 10); // 推断为 i32
println!("结果: {}", result);

// 后续调用必须使用相同类型
// let result2 = add(5.0, 10.0); // 编译错误!
}

显式类型注解

fn main() {
// 显式指定参数类型
let add = |x: i32, y: i32| -> i32 { x + y };

// 或者通过变量类型指定
let multiply: fn(f64, f64) -> f64 = |x, y| x * y;

println!("加法: {}", add(2, 3));
println!("乘法: {}", multiply(2.5, 4.0));
}

闭包作为参数

接受闭包的函数

fn apply_operation<F>(x: i32, y: i32, op: F) -> i32
where
F: Fn(i32, i32) -> i32,
{
op(x, y)
}

fn main() {
let add = |x, y| x + y;
let multiply = |x, y| x * y;

println!("加法: {}", apply_operation(5, 3, add));
println!("乘法: {}", apply_operation(5, 3, multiply));

// 直接传递闭包
println!("减法: {}", apply_operation(5, 3, |x, y| x - y));
}

不同的闭包 trait

// Fn - 可以多次调用,只能不可变借用捕获的值
fn call_multiple_times<F>(f: F)
where
F: Fn() -> String,
{
println!("第一次: {}", f());
println!("第二次: {}", f());
}

// FnMut - 可以多次调用,可以可变借用捕获的值
fn call_with_mutation<F>(mut f: F)
where
F: FnMut() -> i32,
{
println!("第一次: {}", f());
println!("第二次: {}", f());
}

// FnOnce - 只能调用一次,可以获取捕获值的所有权
fn call_once<F>(f: F) -> String
where
F: FnOnce() -> String,
{
f()
}

fn main() {
let name = "Rust".to_string();

// Fn 示例
let greet = || format!("Hello, {}!", name);
call_multiple_times(greet);

// FnMut 示例
let mut counter = 0;
let mut increment = || {
counter += 1;
counter
};
call_with_mutation(increment);

// FnOnce 示例
let message = "Goodbye".to_string();
let consume = move || format!("{}, {}!", message, name);
println!("一次性调用: {}", call_once(consume));
}

返回闭包

使用 Box

fn make_adder(x: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |y| x + y)
}

fn main() {
let add_5 = make_adder(5);
println!("5 + 3 = {}", add_5(3));
}

使用 impl Trait

fn make_multiplier(x: i32) -> impl Fn(i32) -> i32 {
move |y| x * y
}

fn main() {
let multiply_by_3 = make_multiplier(3);
println!("3 * 4 = {}", multiply_by_3(4));
}

实际应用示例

事件处理

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

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

fn handle_event(&self, event: &str) {
(self.callback)(event);
}
}

fn main() {
let handler = EventHandler::new(|event| {
println!("处理事件: {}", event);
});

handler.handle_event("用户点击");
handler.handle_event("页面加载");
}

配置和初始化

struct Config {
debug: bool,
max_connections: usize,
}

fn configure_app<F>(configurator: F) -> Config
where
F: FnOnce(&mut Config),
{
let mut config = Config {
debug: false,
max_connections: 100,
};

configurator(&mut config);
config
}

fn main() {
let config = configure_app(|cfg| {
cfg.debug = true;
cfg.max_connections = 200;
});

println!("Debug: {}, Max connections: {}",
config.debug, config.max_connections);
}

延迟计算

struct LazyValue<F, T>
where
F: FnOnce() -> T,
{
computation: Option<F>,
value: Option<T>,
}

impl<F, T> LazyValue<F, T>
where
F: FnOnce() -> T,
{
fn new(computation: F) -> Self {
LazyValue {
computation: Some(computation),
value: None,
}
}

fn get(&mut self) -> &T {
if self.value.is_none() {
let computation = self.computation.take().unwrap();
self.value = Some(computation());
}
self.value.as_ref().unwrap()
}
}

fn main() {
let mut lazy_sum = LazyValue::new(|| {
println!("执行昂贵的计算...");
(1..=1000).sum::<i32>()
});

println!("第一次获取: {}", lazy_sum.get());
println!("第二次获取: {}", lazy_sum.get()); // 不会重新计算
}

性能考虑

零成本抽象

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

// 使用闭包的迭代器链
let sum: i32 = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * x)
.sum();

println!("偶数平方和: {}", sum);

// 编译器会优化为类似这样的循环:
// let mut sum = 0;
// for &x in &numbers {
// if x % 2 == 0 {
// sum += x * x;
// }
// }
}

内联优化

#[inline]
fn apply_twice<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(f(x))
}

fn main() {
let double = |x| x * 2;
let result = apply_twice(double, 5); // 可能被内联为 ((5 * 2) * 2)
println!("结果: {}", result);
}

常见错误和解决方案

1. 借用检查错误

fn main() {
let mut x = 5;

// 错误:同时存在可变和不可变借用
// let immutable_closure = || println!("x = {}", x);
// let mutable_closure = || { x += 1; };

// 解决方案:分开使用
{
let immutable_closure = || println!("x = {}", x);
immutable_closure();
}

{
let mut mutable_closure = || { x += 1; };
mutable_closure();
}

println!("最终 x = {}", x);
}

2. 生命周期问题

// 错误的做法
// fn create_closure() -> impl Fn() -> i32 {
// let x = 42;
// || x // 错误:x 的生命周期不够长
// }

// 正确的做法
fn create_closure() -> impl Fn() -> i32 {
let x = 42;
move || x // 使用 move 获取所有权
}

fn main() {
let closure = create_closure();
println!("结果: {}", closure());
}

闭包是 Rust 中强大而灵活的特性,掌握它们对于编写高效、表达力强的代码至关重要。下一节我们将深入学习闭包的捕获机制。