知识点1:闭包基本语法
闭包是能捕获周围环境中变量的匿名函数:
rust
fn main() {
// 完整写法
let add_one = |x: i32| -> i32 { x + 1 };
println!("{}", add_one(5)); // 6
// 简写:类型可以省略(编译器会推断)
let double = |x| x * 2;
println!("{}", double(5)); // 10
// 无参数
let greet = || println!("你好!");
greet();
// 多参数
let add = |a, b| a + b;
println!("{}", add(3, 4)); // 7
// 多行闭包需要花括号
let process = |x: i32| {
let doubled = x * 2;
let result = doubled + 1;
result
};
println!("{}", process(10)); // 21
}
知识点2:捕获环境变量
闭包可以三种方式捕获环境变量:
rust
fn main() {
let name = String::from("Rust");
// 1. 不可变借用 &(默认)
let greet = || println!("你好, {}", name);
greet();
println!("name 还能用: {}", name); // ✅ name 只是被借用
// 2. 可变借用 &mut
let mut count = 0;
let mut increment = || {
count += 1;
println!("计数: {}", count);
};
increment(); // 计数: 1
increment(); // 计数: 2
// println!("{}", count); // ❌ count 被可变借用中
// 3. 获取所有权 move
let data = vec![1, 2, 3];
let take_ownership = move || {
println!("闭包拥有: {:?}", data);
};
take_ownership();
// println!("{:?}", data); // ❌ data 的所有权已转移给闭包
}
知识点3:闭包作为参数
rust
// 接受闭包作为参数
// Fn<F> 表示不可变借用环境的闭包
fn apply<F>(f: F, value: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(value)
}
// FnMut<F> 表示可变借用环境的闭包
fn apply_mut<F>(mut f: F, value: i32) -> i32
where
F: FnMut(i32) -> i32,
{
f(value)
}
// FnOnce<F> 表示获取所有权的闭包(只能调用一次)
fn apply_once<F>(f: F, value: i32) -> i32
where
F: FnOnce(i32) -> i32,
{
f(value)
}
fn main() {
let result = apply(|x| x * 2, 5);
println!("{}", result); // 10
let mut total = 0;
let result = apply_mut(|x| {
total += x;
total
}, 5);
println!("{}", result); // 5
// move 闭包(获取所有权)
let data = String::from("hello");
let result = apply_once(move |x| {
println!("data = {}, x = {}", data, x);
x
}, 42);
println!("{}", result); // 42
// println!("{}", data); // ❌ data 已被 move 进闭包
}
知识点4:闭包返回闭包
返回闭包需要用 Box 或 impl Fn:
rust
// 方法1:用 impl Fn(推荐)
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y // move 把 x 的所有权移入闭包
}
// 方法2:用 Box<dyn Fn>(需要动态分发)
fn make_multiplier(x: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |y| x * y)
}
fn main() {
let add_5 = make_adder(5);
println!("{}", add_5(10)); // 15
let mul_3 = make_multiplier(3);
println!("{}", mul_3(10)); // 30
}
知识点5:闭包与迭代器
闭包最常见的用途是配合迭代器:
rust
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map:对每个元素做变换
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
println!("翻倍: {:?}", doubled);
// filter:过滤元素
let evens: Vec<&i32> = numbers.iter().filter(|x| *x % 2 == 0).collect();
println!("偶数: {:?}", evens);
// fold:累积计算
let sum: i32 = numbers.iter().fold(0, |acc, x| acc + x);
println!("总和: {}", sum);
// any / all
let has_even = numbers.iter().any(|x| x % 2 == 0);
let all_positive = numbers.iter().all(|x| *x > 0);
println!("有偶数: {}", has_even);
println!("全正数: {}", all_positive);
// find
let first_big = numbers.iter().find(|&&x| x > 7);
println!("第一个大于7的: {:?}", first_big);
// chain + 闭包
let a = vec![1, 2, 3];
let b = vec![4, 5, 6];
let combined: Vec<i32> = a.iter().chain(b.iter()).map(|x| x * 10).collect();
println!("合并: {:?}", combined);
}
核心规则
概念 写法
闭包语法 params expression 或 params { statements }
类型标注 x: i32 -> i32 { x + 1 }
不可变借用 Fn trait(默认)
可变借用 FnMut trait
获取所有权 FnOnce trait + move 关键字
move 闭包 move { ... }
返回闭包 -> impl Fn(T) -> U 或 -> Box<dyn Fn(T) -> U>
配合迭代器 .map(), .filter(), .fold(), .any(), .find()
动手试试
补全下面的代码:
rust
// 补全:实现函数 apply_operations
// 接受一个整数向量 vec 和一个闭包 operations
// operations 接受一个 i32,返回一个 i32
// 对 vec 中每个元素应用 operations,返回新的向量
fn apply_operations<F>(vec: Vec<i32>, operations: F) -> Vec<i32>
where
F: Fn(i32) -> i32,
{
// 补全:用 iter + map + collect 实现
todo!()
}
// 补全:实现函数 make_counter
// 返回一个闭包,每次调用时内部计数器加1并返回当前值
// 初始值为 start
fn make_counter(start: i32) -> impl FnMut() -> i32 {
// 补全:用 move 捕获一个可变计数器
// 提示:需要一个 mut 变量在闭包内部
todo!()
}
// 补全:实现函数 compose
// 接受两个闭包 f 和 g,返回一个新的闭包
// 新闭包对输入 x 先应用 g,再应用 f(即 f(g(x)))
fn compose<F, G>(f: F, g: G) -> impl Fn(i32) -> i32
where
F: Fn(i32) -> i32,
G: Fn(i32) -> i32,
{
// 补全:返回一个闭包,先调用 g 再调用 f
todo!()
}
fn main() {
// 测试 apply_operations
let nums = vec![1, 2, 3, 4, 5];
// 补全:对每个元素求平方
let squares = apply_operations(nums.clone(), |x| x * x);
println!("平方: {:?}", squares);
// 期望: [1, 4, 9, 16, 25]
// 补全:对每个元素加10再乘2
let transformed = apply_operations(nums.clone(), |x| (x + 10) * 2);
println!("变换: {:?}", transformed);
// 期望: [22, 24, 26, 28, 30]
// 测试 make_counter
// 补全:创建一个从0开始的计数器
// 连续调用3次,打印每次的值
// 期望: 1, 2, 3
// 测试 compose
// 补全:创建两个闭包
// double: x -> x * 2
// add_one: x -> x + 1
// 用 compose 组合:先 double 再 add_one,即 add_one(double(x)) = 2x + 1
// 对 5 应用,期望输出: 11
// 再组合:先 add_one 再 double,即 double(add_one(x)) = 2(x+1)
// 对 5 应用,期望输出: 12
}
期望输出:
平方: 1, 4, 9, 16, 25
变换: 22, 24, 26, 28, 30
计数: 1
计数: 2
计数: 3
组合1(25+1): 11
组合2(2(5+1)): 12
写好了贴过来,没问题就进入第18课:智能指针 Smart Pointers 👋
贴代码
下面是完整可运行的参考答案,闭包三大经典练习:map变换、状态闭包(FnMut)、函数组合 compose。
rust
// 对向量每个元素执行闭包变换
fn apply_operations<F>(vec: Vec<i32>, operations: F) -> Vec<i32>
where
F: Fn(i32) -> i32,
{
vec.into_iter().map(operations).collect()
}
// 返回一个带状态的计数器闭包(FnMut)
fn make_counter(start: i32) -> impl FnMut() -> i32 {
let mut count = start;
move || {
count += 1;
count
}
}
// 函数组合:compose(f,g) => f(g(x))
fn compose<F, G>(f: F, g: G) -> impl Fn(i32) -> i32
where
F: Fn(i32) -> i32,
G: Fn(i32) -> i32,
{
move |x| f(g(x))
}
fn main() {
// 测试 apply_operations
let nums = vec![1, 2, 3, 4, 5];
// 每个元素求平方
let squares = apply_operations(nums.clone(), |x| x * x);
println!("平方: {:?}", squares);
// 每个元素加10再乘2
let transformed = apply_operations(nums.clone(), |x| (x + 10) * 2);
println!("变换: {:?}", transformed);
// 测试 make_counter,从0开始
let mut counter = make_counter(0);
println!("{}", counter());
println!("{}", counter());
println!("{}", counter());
// 测试 compose:先 double,再 add_one => add_one(double(x))
let double = |x| x * 2;
let add_one = |x| x + 1;
let c1 = compose(add_one, double);
println!("compose1(5) = {}", c1(5));
// 先 add_one,再 double
let c2 = compose(double, add_one);
println!("compose2(5) = {}", c2(5));
}
运行输出
plaintext
平方: 1, 4, 9, 16, 25
变换: 22, 24, 26, 28, 30
1
2
3
compose1(5) = 11
compose2(5) = 12
知识点详解
1. apply_operations
rust
vec.into_iter().map(operations).collect()
- into_iter :获取所有权的迭代器,消费 Vec;
- map(operations) :迭代器适配器,把闭包应用到每一个元素;
- collect() :把迭代器结果收集回新的 Vec。
- F: Fn(i32) -> i32 :不可变闭包,不能捕获可变环境,只读。
区分:
- Fn:捕获环境(只读),可以多次调用;
- FnMut:可以修改捕获的变量;
- FnOnce:只能调用一次(拿走捕获变量所有权)。
2. make_counter
rust
fn make_counter(start:i32) -> impl FnMut()->i32 {
let mut count = start;
move || { count +=1; count }
}
- move :把外部变量 count 移动进闭包,闭包拥有 count;
- 返回类型是 impl FnMut() ,因为闭包内部修改 count;
- 调用者那边必须声明 let mut counter ,才能调用。
3. compose 函数组合
rust
move |x| f(g(x))
compose(f,g) 含义:先执行 g,再执行 f,数学记号: (f ∘ g)(x) = f(g(x)) 。
做完闭包这一关,Rust基础板块(泛型、生命周期、trait、闭包、迭代器)基本全部通关。