Rust:Fn,FnMut,FnOnce

闭包只有这三种模式,即
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);
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值