闭包只有这三种模式,即
Fn:它只能从闭包环境中不可变借用相关变量;
FnMut:它能可变借用捕获的变量,并改变闭包环境;
FnOnce:它捕获的闭包环境变量,闭包本身只能用一次,不是说闭包中的变量被一次性消费,而是指闭包本身;
为什么会引出Fn,FnMut,FnOnce?
一、Fn、FnMut和FnMut
1、Fn
看一下以下的例子:
fn call_Fn(x: i32, f: impl Fn(i32) -> i32) {
f(x);
}
fn main() {
let j = 5;
let g = |i: i32| i + j;
call_Fn(2, g);
thread::sleep_ms(500000);
}
为什么用Fn,而不是其它?FnMut? FnOnce?
let f_0 = |i: i32| i+ j;
在闭包g中,输入参数是i32,返回参数为i32,环境变量为j,在这个过程中,环境变量并没有改变。因此,用Fn是合适的; 用FnMut是不合适的。
2、FnMut
我把上面的例子改一下:
fn call_FnMut(x: i32, mut f: impl FnMut(i32) -> ()) {
f(x);
}
fn main() {
let mut j = 5;
let g = |i: i32| j = i + j;
call_Fn(2, g);
//call_Fn(2, g); //error,只能调用一次
thread::sleep_ms(500000);
}
同样,
let f_0 = |i: i32| j = i + j;
输入参数为i,i32; 环境变量j,j为mut类型。这个闭包改变了j。
因此,适合的就是FnMut,不适用用Fn。但改成FnOnce也是可以的,即:
fn call_FnOnce(x: i32, mut f: impl FnOnce(i32) -> ()) {
f(x);
}
3、FnOnce
fn call<F:FnOnce()>(f:F){
f()
}
fn main() {
let mut v = vec![];
let g = ||{
v.push(1);
println!("v:{:?}",v);
v
};
//call(g);
//call(g); 只能调用一次,不能调用第二次
g();
}
如果call中换成Fn或FnMut,都是不行的 。g()或call()只能调用一次;
二、共同适用的情况
有意思的是,上面FnOnce 的例子,其实也可以用Fn;
fn call_Fn(mut f: impl Fn() -> ()) -> () {
f();
}
fn main() {
let sy_time0 = SystemTime::now();
let s = "i love rust!";
let g = || println!("{}", s);
call_Fn(g);
thread::sleep_ms(500000);
}
包括,加一行代码,Fn与FnMut都适合。
fn call_FnOnce(i: usize, mut f: impl FnOnce(usize) -> ()) -> () {
f(i);
}
fn main() {
let sy_time0 = SystemTime::now();
let s = "abc";
let g = |i: usize| {
println!("{}", s);
s.repeat(i);
};
call_FnOnce(2, g);
thread::sleep_ms(500000);
}
还有:
fn call_FnOnce(mut f: impl FnOnce() -> ()) -> () {
f();
}
fn main() {
let s = "i love rust!";
let g = || {
let ss ="hello".to_string() + s;
println!("{:?}", ss);
};
call_FnOnce(g);
call_FnOnce(g);
println!("s:{:?}",s);
call_FnOnce(g);
g();
g();
thread::sleep_ms(500000);
}
同样,
此时适合用FnOnce,也适合用Fn; 即:
fn call_Fn(mut f: impl Fn() -> ()) -> () {
f();
}
三、闭包中参数可变时
fn call_Fn(x: i32, f: impl Fn(i32) -> ()) -> () {
f(x);
}
fn main() {
let sy_time0 = SystemTime::now();
let j = 5;
let g = |i: i32| i = i + j; // i需要如何改变,才能通过?
call_Fn(2, g);
thread::sleep_ms(500000);
}
为什么上面的代码是不行?
let g = |i: i32| i = i + j;
从闭包来看,闭包的参数已经发生变化,因此需要改成以下模式:
let g = |mut i: i32| i = i + j;
就可以了。
上面的也可以写成FnMut的形式:
fn call_FnMut(x: i32, mut f: impl FnMut(i32) -> ()) -> () {
f(x);
}
fn main() {
let sy_time0 = SystemTime::now();
let j = 5;
let mut g = |mut i: i32| i = i + j;
call_FnMut(2, g);
thread::sleep_ms(500000);
}