Rust学习笔记(三十九)闭包(下)
继续解决三十八节中重复调用闭包的问题:创建一个结构体,它持有闭包及其调用结果:只有在需要结果时才会执行该闭包可以缓存结果,避免再次使用时的重复调用
这个模式通常叫做记忆化(memoization)或惰性求值延迟计算(lazyevaluation)结构体持有闭包
因为结构体的定义需要指定所有字段的类型,所以需要指明闭包的类型。每个闭包实例都有自己唯一的匿名类型,即使两个闭包的签名完全一样。所以需要使用泛型和traitbound。Fn系列trait
Fn系列trait由标准库提供,所有闭包都至少实现了以下trait之一:FnFnMutFnOnce
下面使用Fntrait修改之前的例子:srcmain。rsusestd::{thread,time::Duration};structCacherFwhereF:Fn(u32)u32,泛型F代表闭包的类型,它是一个接收u32并返回u32的闭包,Fn(u32)u32是一个特殊的语法,只适用于Fn,FnOnce和FnMut{calculation:F,value:Optionu32,在运行闭包之前,value是None;运行闭包之后,value就用来保持闭包的返回值}implFCacherFwhereF:Fn(u32)u32,{实现Cacher的关联函数,用来构造一个Cacher的实例fnnew(calculation:F)CacherF{Cacher{calculation,value:None,}}定义方法,该方法首先判断Cacher中的value是否有值若有值则说明闭包已经执行过了,可以直接返回缓存的值没有值说明是第一次使用,这时可以调用闭包,然后缓存其值并返回fnvalue(mutself,arg:u32)u32{matchself。value{Some(v)v,None{letv(self。calculation)(arg);self。valueSome(v);v}}}}fngenerateworkout(intensity:u32,randomnumber:u32){因为后面调用的value方法可能会修改expensiveclosure中value的值,所以需要声明为可变的letmutexpensiveclosureCacher::new(num{println!(calculatingslowly。。。);thread::sleep(Duration::fromsecs(2));num});ifintensity25{println!(Today,do{}pushups!,expensiveclosure。value(intensity));println!(Next,do{}situps!,expensiveclosure。value(intensity));}else{ifrandomnumber3{println!(Takeabreaktoday!Remembertostayhydrated!);}else{println!(Today,runfor{}minutes!,expensiveclosure。value(intensity));}}}fnmain(){letsumulateduserspecifiedvalue10;letsimulatedrandomnumber7;generateworkout(sumulateduserspecifiedvalue,simulatedrandomnumber);}
运行以上代码,输出:calculatingslowly。。。Today,do10pushups!Next,do10situps!
根据代码逻辑,expensiveclosure。value(intensity)会执行两次,但是calculatingslowly。。。只输出了一次,说明闭包只执行了一次。使用缓存器(Cacher)实现的限制Cacher实例假定针对不同的参数arg,value方法总会得到同样的值。无论我们传入的值是几,返回总是第一次传入的值。因为根据我们的逻辑,闭包只会执行一次,导致value的值只会更新一次。只能接收一个u32类型参数和u32类型的返回值
针对这些限制,下一节会给出解决方案。