十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【Rust入门知识点学与练】第17课:闭包 Closures

【Rust入门知识点学与练】第17课:闭包 Closures 知识点1闭包基本语法闭包是能捕获周围环境中变量的匿名函数fnmain(){// 完整写法letadd_one|x:i32|-i32{x1};println!({},add_one(5));// 6// 简写类型可以省略编译器会推断letdouble|x|x*2;println!({},double(5));// 10// 无参数letgreet||println!(你好);greet();// 多参数letadd|a,b|ab;println!({},add(3,4));// 7// 多行闭包需要花括号letprocess|x:i32|{letdoubledx*2;letresultdoubled1;result};println!({},process(10));// 21}知识点2捕获环境变量闭包可以三种方式捕获环境变量fnmain(){letnameString::from(Rust);// 1. 不可变借用 默认letgreet||println!(你好, {},name);greet();println!(name 还能用: {},name);// ✅ name 只是被借用// 2. 可变借用 mutletmutcount0;letmutincrement||{count1;println!(计数: {},count);};increment();// 计数: 1increment();// 计数: 2// println!({}, count); // ❌ count 被可变借用中// 3. 获取所有权 moveletdatavec![1,2,3];lettake_ownershipmove||{println!(闭包拥有: {:?},data);};take_ownership();// println!({:?}, data); // ❌ data 的所有权已转移给闭包}知识点3闭包作为参数// 接受闭包作为参数// FnF 表示不可变借用环境的闭包fnapplyF(f:F,value:i32)-i32whereF:Fn(i32)-i32,{f(value)}// FnMutF 表示可变借用环境的闭包fnapply_mutF(mutf:F,value:i32)-i32whereF:FnMut(i32)-i32,{f(value)}// FnOnceF 表示获取所有权的闭包只能调用一次fnapply_onceF(f:F,value:i32)-i32whereF:FnOnce(i32)-i32,{f(value)}fnmain(){letresultapply(|x|x*2,5);println!({},result);// 10letmuttotal0;letresultapply_mut(|x|{totalx;total},5);println!({},result);// 5// move 闭包获取所有权letdataString::from(hello);letresultapply_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|xy// move 把 x 的所有权移入闭包}// 方法2用 Boxdyn Fn需要动态分发fnmake_multiplier(x:i32)-BoxdynFn(i32)-i32{Box::new(move|y|x*y)}fnmain(){letadd_5make_adder(5);println!({},add_5(10));// 15letmul_3make_multiplier(3);println!({},mul_3(10));// 30}知识点5闭包与迭代器闭包最常见的用途是配合迭代器fnmain(){letnumbersvec![1,2,3,4,5,6,7,8,9,10];// map对每个元素做变换letdoubled:Veci32numbers.iter().map(|x|x*2).collect();println!(翻倍: {:?},doubled);// filter过滤元素letevens:Veci32numbers.iter().filter(|x|*x%20).collect();println!(偶数: {:?},evens);// fold累积计算letsum:i32numbers.iter().fold(0,|acc,x|accx);println!(总和: {},sum);// any / alllethas_evennumbers.iter().any(|x|x%20);letall_positivenumbers.iter().all(|x|*x0);println!(有偶数: {},has_even);println!(全正数: {},all_positive);// findletfirst_bignumbers.iter().find(|x|x7);println!(第一个大于7的: {:?},first_big);// chain 闭包letavec![1,2,3];letbvec![4,5,6];letcombined:Veci32a.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 或 - Boxdyn Fn(T) - U配合迭代器 .map(), .filter(), .fold(), .any(), .find()动手试试补全下面的代码// 补全实现函数 apply_operations// 接受一个整数向量 vec 和一个闭包 operations// operations 接受一个 i32返回一个 i32// 对 vec 中每个元素应用 operations返回新的向量fnapply_operationsF(vec:Veci32,operations:F)-Veci32whereF: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))fncomposeF,G(f:F,g:G)-implFn(i32)-i32whereF:Fn(i32)-i32,G:Fn(i32)-i32,{// 补全返回一个闭包先调用 g 再调用 ftodo!()}fnmain(){// 测试 apply_operationsletnumsvec![1,2,3,4,5];// 补全对每个元素求平方letsquaresapply_operations(nums.clone(),|x|x*x);println!(平方: {:?},squares);// 期望: [1, 4, 9, 16, 25]// 补全对每个元素加10再乘2lettransformedapply_operations(nums.clone(),|x|(x10)*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(x1)// 对 5 应用期望输出: 12}期望输出平方: [1, 4, 9, 16, 25]变换: [22, 24, 26, 28, 30]计数: 1计数: 2计数: 3组合1(251): 11组合2(2(51)): 12写好了贴过来没问题就进入第18课智能指针 Smart Pointers 贴代码下面是完整可运行的参考答案闭包三大经典练习map变换、状态闭包FnMut、函数组合 compose。// 对向量每个元素执行闭包变换fnapply_operationsF(vec:Veci32,operations:F)-Veci32whereF:Fn(i32)-i32,{vec.into_iter().map(operations).collect()}// 返回一个带状态的计数器闭包FnMutfnmake_counter(start:i32)-implFnMut()-i32{letmutcountstart;move||{count1;count}}// 函数组合compose(f,g) f(g(x))fncomposeF,G(f:F,g:G)-implFn(i32)-i32whereF:Fn(i32)-i32,G:Fn(i32)-i32,{move|x|f(g(x))}fnmain(){// 测试 apply_operationsletnumsvec![1,2,3,4,5];// 每个元素求平方letsquaresapply_operations(nums.clone(),|x|x*x);println!(平方: {:?},squares);// 每个元素加10再乘2lettransformedapply_operations(nums.clone(),|x|(x10)*2);println!(变换: {:?},transformed);// 测试 make_counter从0开始letmutcountermake_counter(0);println!({},counter());println!({},counter());println!({},counter());// 测试 compose先 double再 add_one add_one(double(x))letdouble|x|x*2;letadd_one|x|x1;letc1compose(add_one,double);println!(compose1(5) {},c1(5));// 先 add_one再 doubleletc2compose(double,add_one);println!(compose2(5) {},c2(5));}运行输出plaintext平方: [1, 4, 9, 16, 25]变换: [22, 24, 26, 28, 30]123compose1(5) 11compose2(5) 12知识点详解1. apply_operationsvec.into_iter().map(operations).collect()into_iter 获取所有权的迭代器消费 Vecmap(operations) 迭代器适配器把闭包应用到每一个元素collect() 把迭代器结果收集回新的 Vec。F: Fn(i32) - i32 不可变闭包不能捕获可变环境只读。区分Fn捕获环境只读可以多次调用FnMut可以修改捕获的变量FnOnce只能调用一次拿走捕获变量所有权。2. make_counterfnmake_counter(start:i32)-implFnMut()-i32{letmutcountstart;move||{count1;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、闭包、迭代器基本全部通关。
返回列表