Rust闭包学习心得
作者:互联网
move
&&Fn
/FnMut
/FnOnce
move
影响的是闭包如何捕获环境中的变量 , 影响的是闭包初始化之后外部变量是否还能继续使用。
闭包体内如何使用变量决定了闭包实现的是哪种Trait,影响了闭包自身的类型。但是闭包体使用的变量的方式同时也影响了闭包初始化之后外部变量能否继续使用。
也就是说环境变量能否在闭包初始化之后继续使用实际上被两点所影响,闭包是否有move参数,以及闭包体内使用变量的方式。所以可能会出现这种情况:闭包体内对变量的使用是最轻微的不可变引用,并未拿走所有权,但是初始化闭包的时候加了move参数,这导致了闭包在初始化之后环境变量就会不可用。
如何使用变量决定了闭包实现的是哪种Trait是什么意思?
- 闭包的方法通过不可变引用访问其捕获的变量的闭包实现了Fn
- 闭包的方法通过可变引用访问其捕获变量的闭包实现了FnMut
- 闭包的方法若只能被调用一次,即实现了FnOnce。(说明闭包消耗了捕获的变量)。
Rust会根据闭包体和是否有move
来推导如何捕获环境中变量,闭包捕获环境中变量的模式优先级顺序为:不可变借用,可变借用,移动。
fn main() {
// 下面这段代码的闭包体中它没有获得变量的所有权,没有转让变量的所有权,
// 所以这个闭包的类型是Fn()->Box<String>
// 在闭包捕获完s之后原来的变量仍然能够使用
let s = String::from("gef");
let t = || {
let s = &s;
let g = Box::new((*s).clone());
g
};
println!("{}", s);
// 这个闭包的闭包体和上面仅有一处不同,那就是加了move,这会强行拿走s的所有权,即影响了捕获方式。
// ****即使**** 闭包体看起来是用最普通的不可变引用来操作的环境中的变量,也看起来不该拿走变量的所有权
let s = String::from("gef");
let t = move || {
let s = &s;
let g = Box::new((*s).clone());
g
};
// 不加注释会编译失败,s所有权已经被拿走了
// println!("{}",s);
// 即使用了move,这个闭包的类型依然是Fn()->Box<String>
let fuck:Box<dyn Fn()->Box<String>>=Box::new(t);
let s=String::from("gef");
let t=||s;
// 这行代码无法编译,因为s的所有权被移动到闭包里面去了,但如果闭包体是 &s,则可以编译
// println!("{}",s)
// 无论用不用move,这个闭包的类型都是FnOnce()->String, 因为闭包实现哪种Trait只和如何使用捕获到的变量有关系
// 但是闭包体的代码在一定程度上影响了变量如何捕获,Rust会通过推导来决定要不要把变量的所有权转移到闭包体内
// 对于没有实现Copy Trait的类型来说,不加move的话,环境变量会不会被拿走所有权,取决于闭包体内的使用方式
// **但是如果加了move,无论闭包体怎么写,被捕获的环境变量(未实现Copy Trait)一定会被拿走所有权**
}
闭包的初始化行为只会执行一次,就是在闭包定义处,而不是在闭包执行处。在这个过程中会进行变量的捕获,且仅在闭包的定义处捕获一次,后期执行处不再进行捕获操作。
fn main(){
let s=String::from("111");
let g=||s;
g();
let g=||s; // 标签:闭包,变量,Trait,学习心得,let,Rust,fn,impl
来源: https://www.cnblogs.com/hardsinging/p/15976655.html