Rust中的Fn、FnOnce、FnMut闭包详解

闭包会自动实现FnFnOnceFnMut这三个trait中的任意一个,这取决于闭包在调用过程中对捕获变量的使用;

先了解Rust中的借用规则:

  • 在给定的作用域内,可以有多个不可变引用;
  • 在给定的作用域内,只允许有一个可变引用;
  • 在给定的作用域内,不允许同时存在可变引用和不可变引用;

Fn 闭包

如果闭包捕获了外部环境中的变量,并且在闭包内部仅对这些变量进行不可变访问,则闭包会自动实现Fntrait;

实现了Fntrait的闭包能被多次调用;

fn main() {
    let str = "hello".to_string();

    let closure = || {
        println!("{}",str);
    };

    call_closure(closure);
    call_closure(closure);
    call_closure(closure);
    call_closure(closure);
    println!("{}",str);

}

fn call_closure(f: impl Fn()){f();}

这段代码中,闭包内部只对捕获到的变量str进行了不可变访问,所以它将自动实现Fntrait;

根据借用规则,允许存在多个str的不可变引用,所以这个闭包能被多次调用;

FnOnce 闭包

如果闭包捕获了外部环境中的变量,并且在闭包内部对其中至少一个变量进行了所有权转移,则闭包会自动实现FnOncetrait;

实现了FnOncetrait的闭包只允许被调用一次;

fn main() {
    let str = "hello".to_string();

    let closure = move || {
        let str2 = str;
        println!("{}",str2);
    };

    call_closure(closure);
    //call_closure(closure); //只允许调用一次
    //println!("{}",str); //所有权被转移,无法再访问
    
}

fn call_closure(f: impl FnOnce()){f();}

这段代码中,闭包内部将捕获变量str的所有权转移到了str2move关键字表示闭包会获取捕获变量的所有权,而不仅仅只是引用他们;

由于该闭包发生了捕获变量的所有权转移,所以它会自动实现FnOncetrait;

由于str的所有权被转移,str将被标记为不可使用,这也是FnOnce闭包只能被调用一次的原因;

FnMut 闭包

如果闭包捕获了外部环境中的变量,并且在闭包内部对其中至少一个变量进行了可变访问,则闭包会自动实现FnMuttrait;

实现了FnMuttrait的闭包允许被多次调用;

fn main() {
    let mut str = "hello".to_string();

    let mut closure = || {
        str.push_str(" world");
        println!("{}",str);
    };

    //println!("{}",str); //不可访问

    call_closure(&mut closure);
    call_closure(&mut closure);
    call_closure(&mut closure);
    
    println!("{}",str); //可以访问

}

fn call_closure(f: &mut impl FnMut()){f();}

这段代码中,闭包内部发生了对捕获变量str的可变访问,该闭包将自动实现FnMuttrait;

9行代码中,str不可变引用与闭包中捕获变量str的可变引用作用域重叠,违反借用规则;

15行代码中,str不可变引用与闭包中捕获变量str的可变引用作用域没有重叠,没有违反借用规则;

原创内容,如需转载,请注明出处;

本文地址: https://www.perfcode.com/p/fn-fnonce-fnmut.html

分类: 计算机技术
推荐阅读:
PySide6 使用QIcon为按钮添加图标 在PySide6中为按钮添加图标,可以使用QIcon类来加载图标文件,并使用QPushButton类的setIcon()方法将图标设置给按钮;
Python实现节点 在某些情况下,无法在连续的内存块中为数据分配内存。 因此,我们在数据元素中记录下一个数据的内存地址;此类结构称为指针。 但是在Python中,我们将它们称为节点。
如何忽略pymysql警告信息 在python中使用pymysql库时,你可能会碰到由pymysql生成的警告信息;
python @staticmethod装饰器 @staticmethod 是一个装饰器,用于声明一个静态方法。静态方法是一个属于类而不是属于实例的方法,可以直接通过类名调用,而不需要创建实例。
1400多万个弱口令密码字典下载 该密码字典包含1400多万个密码,包含弱密码、常用密码、短密码等组合,适合暴力猜解使用;该文件来源于Kali系统下的密码字典文件;
Python this模块的加密原理 this模块的代码(this模块位于Python安装目录/lib下)。定义了2个变量;字符串s和字典d(被定义两次);s很明显是一段密文,d则是密码字典,key和value对应的是密文和原文;chr((i+13)%26 +c) 则是加密算法,其原理是通过向字典d写入KEY为字符A~Z,VALUE为加密后的字符。然后通过字典遍历的方法,匹配出正确的字符。