知识点1:闭包基本语法
闭包是能捕获周围环境中变量的匿名函数:
fnmain(){// 完整写法letadd_one=|x:i32|->i32{x+1};println!("{}",add_one(5));// 6// 简写:类型可以省略(编译器会推断)letdouble=|x|x*2;println!("{}",double(5));// 10// 无参数letgreet=||println!("你好!");greet();// 多参数letadd=|a,b|a+b;println!("{}",add(3,4));// 7// 多行闭包需要花括号letprocess=|x:i32|{letdoubled=x*2;letresult=doubled+1;result};println!("{}",process(10));// 21}知识点2:捕获环境变量
闭包可以三种方式捕获环境变量:
fnmain(){letname=String::from("Rust");// 1. 不可变借用 &(默认)letgreet=||println!("你好, {}",name);greet();println!("name 还能用: {}",name);// ✅ name 只是被借用// 2. 可变借用 &mutletmutcount=0;letmutincrement=||{count+=1;println!("计数: {}",count);};increment();// 计数: 1increment();// 计数: 2// println!("{}", count); // ❌ count 被可变借用中// 3. 获取所有权 moveletdata=vec![1,2,3];lettake_ownership=move||{println!("闭包拥有: {:?}",data);};take_ownership();// println!("{:?}", data); // ❌ data 的所有权已转移给闭包}知识点3:闭包作为参数
// 接受闭包作为参数// Fn<F> 表示不可变借用环境的闭包fnapply<F>(f:F,value:i32)->i32whereF:Fn(i32)->i32,{f(value)}// FnMut<F> 表示可变借用环境的闭包fnapply_mut<F>(mutf:F,value:i32)->i32whereF:FnMut(i32)->i32,{f(value)}// FnOnce<F> 表示获取所有权的闭包(只能调用一次)fnapply_once<F>(f:F,value:i32)->i32whereF:FnOnce(i32)->i32,{f(value)}fnmain(){letresult=apply(|x|x*2,5);println!("{}",result);// 10letmuttotal=0;letresult=apply_mut(|x|{total+=x;total},5);println!("{}",result);// 5// move 闭包(获取所有权)letdata=String::from("hello");letresult=apply_once(move|x|{println!("data = {}, x = {}",data,x);x},42);println!("{}",result);// 42// println!("{}", data); // ❌ data 已被 move 进闭包}知识点4:闭包返回闭包
返回闭包需要用 Box 或 impl Fn:
// 方法1:用 impl Fn(推荐)fnmake_adder(x:i32)->implFn(i32)->i32{move|y|x+y// move 把 x 的所有权移入闭包}// 方法2:用 Box<dyn Fn>(需要动态分发)fnmake_multiplier(x:i32)->Box<dynFn(i32)->i32>{Box::new(move|y|x*y)}fnmain(){letadd_5=make_adder(5);println!("{}",add_5(10));// 15letmul_3=make_multiplier(3);println!("{}",mul_3(10));// 30}知识点5:闭包与迭代器
闭包最常见的用途是配合迭代器:
fnmain(){letnumbers=vec![1,2,3,4,5,6,7,8,9,10];// map:对每个元素做变换letdoubled:Vec<i32>=numbers.iter().map(|x|x*2).collect();println!("翻倍: {:?}",doubled);// filter:过滤元素letevens:Vec<&i32>=numbers.iter().filter(|x|*x%2==0).collect();println!("偶数: {:?}",evens);// fold:累积计算letsum:i32=numbers.iter().fold(0,|acc,x|acc+x);println!("总和: {}",sum);// any / alllethas_even=numbers.iter().any(|x|x%2==0);letall_positive=numbers.iter().all(|x|*x>0);println!("有偶数: {}",has_even);println!("全正数: {}",all_positive);// findletfirst_big=numbers.iter().find(|&&x|x>7);println!("第一个大于7的: {:?}",first_big);// chain + 闭包leta=vec![1,2,3];letb=vec![4,5,6];letcombined: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()
动手试试
补全下面的代码:
// 补全:实现函数 apply_operations// 接受一个整数向量 vec 和一个闭包 operations// operations 接受一个 i32,返回一个 i32// 对 vec 中每个元素应用 operations,返回新的向量fnapply_operations<F>(vec:Vec<i32>,operations:F)->Vec<i32>whereF:Fn(i32)->i32,{// 补全:用 iter + map + collect 实现todo!()}// 补全:实现函数 make_counter// 返回一个闭包,每次调用时内部计数器加1并返回当前值// 初始值为 startfnmake_counter(start:i32)->implFnMut()->i32{// 补全:用 move 捕获一个可变计数器// 提示:需要一个 mut 变量在闭包内部todo!()}// 补全:实现函数 compose// 接受两个闭包 f 和 g,返回一个新的闭包// 新闭包对输入 x 先应用 g,再应用 f(即 f(g(x)))fncompose<F,G>(f:F,g:G)->implFn(i32)->i32whereF:Fn(i32)->i32,G:Fn(i32)->i32,{// 补全:返回一个闭包,先调用 g 再调用 ftodo!()}fnmain(){// 测试 apply_operationsletnums=vec![1,2,3,4,5];// 补全:对每个元素求平方letsquares=apply_operations(nums.clone(),|x|x*x);println!("平方: {:?}",squares);// 期望: [1, 4, 9, 16, 25]// 补全:对每个元素加10再乘2lettransformed=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。
// 对向量每个元素执行闭包变换fnapply_operations<F>(vec:Vec<i32>,operations:F)->Vec<i32>whereF:Fn(i32)->i32,{vec.into_iter().map(operations).collect()}// 返回一个带状态的计数器闭包(FnMut)fnmake_counter(start:i32)->implFnMut()->i32{letmutcount=start;move||{count+=1;count}}// 函数组合:compose(f,g) => f(g(x))fncompose<F,G>(f:F,g:G)->implFn(i32)->i32whereF:Fn(i32)->i32,G:Fn(i32)->i32,{move|x|f(g(x))}fnmain(){// 测试 apply_operationsletnums=vec![1,2,3,4,5];// 每个元素求平方letsquares=apply_operations(nums.clone(),|x|x*x);println!("平方: {:?}",squares);// 每个元素加10再乘2lettransformed=apply_operations(nums.clone(),|x|(x+10)*2);println!("变换: {:?}",transformed);// 测试 make_counter,从0开始letmutcounter=make_counter(0);println!("{}",counter());println!("{}",counter());println!("{}",counter());// 测试 compose:先 double,再 add_one => add_one(double(x))letdouble=|x|x*2;letadd_one=|x|x+1;letc1=compose(add_one,double);println!("compose1(5) = {}",c1(5));// 先 add_one,再 doubleletc2=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
vec.into_iter().map(operations).collect()- into_iter :获取所有权的迭代器,消费 Vec;
- map(operations) :迭代器适配器,把闭包应用到每一个元素;
- collect() :把迭代器结果收集回新的 Vec。
- F: Fn(i32) -> i32 :不可变闭包,不能捕获可变环境,只读。
区分:
- Fn:捕获环境(只读),可以多次调用;
- FnMut:可以修改捕获的变量;
- FnOnce:只能调用一次(拿走捕获变量所有权)。
2. make_counter
fnmake_counter(start:i32)->implFnMut()->i32{letmutcount=start;move||{count+=1;count}}- move :把外部变量 count 移动进闭包,闭包拥有 count;
- 返回类型是 impl FnMut() ,因为闭包内部修改 count;
- 调用者那边必须声明 let mut counter ,才能调用。
3. compose 函数组合
move|x|f(g(x))compose(f,g) 含义:先执行 g,再执行 f,数学记号: (f ∘ g)(x) = f(g(x)) 。
做完闭包这一关,Rust基础板块(泛型、生命周期、trait、闭包、迭代器)基本全部通关。