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

分类: 计算机技术
推荐阅读:
Rust debug_assert_eq宏的用法和示例 在Rust语言中,debug_assert_eq宏专门用于进行调试时的值相等性检查。它类似于assert_eq宏,但有一个重要的区别:debug_assert_eq只能在Rust的debug模式下进行检查,这意味着在发布应用程序时,所有的debug_assert_eq检查都会被编译器完全移除,不会影响最终的执行代码。
如何删除一个已发布的crate 实际上,我们可以联系crates.io的技术支持团队,要求删除一些没有意义的crate,这些crate通常没人使用,不包含有意义的代码;
Python delattr()函数 在Python中,delattr()函数用于删除对象的属性。它接受两个参数:第一个参数是要删除属性的对象,第二个参数是要删除的属性的名称。如果对象中不存在指定的属性,将会抛出AttributeError异常。
Linux一条命令删除文件中的重复项 在Linux系统中,如果你想给一个包含重复项(以行为一个单位)的文件去重,那么你只需要一条命令即可实现;
Python webbrowser模块的详细用法 webbrowser是python下一个内置的模块,该模块提供了一个高级接口,使你可以调用计算机中的浏览器以打开基于WEB的文档,比如常见的html网页;
Python实现二分法检索(binary search) 二分法检索(binary search)又称折半检索,二分法检索的基本思想是设字典中的元素从小到大有序地存放在数组(array)中。