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

分类: 计算机技术
推荐阅读:
Python any()函数详细教程 any()函数只接受一个可迭代的类型参数;如果该迭代器的任意一个元素为True,则返回True,否则返回False;如果迭代器为空返回False;
Golang安装gin库的详细教程及错误解决方法 Gin是用Go(Golang)编写的Web框架。 它具有类似于martini的API,其性能比httprouter快40倍。 如果您需要性能和良好的生产率,您会喜欢Gin
让自己的网页在手机QQ中以卡片的方式分享出去 在使用QQ进行聊天的过程中,发送自己网页的链接时(不是打开网站后分享给好友,单单指将网页链接以文本的形式发送出去),默认情况下是不会以卡片形式显示的,那么如何让别人看到的是卡片呢。
C语言isdigit()函数:判断字符是否为数字字符 isdigit()是C语言标准库中的一个函数,用于判断一个字符是否是数字字符(ASCII码为48~57);如果传入的字符参数是一个数字字符(0~9),则返回非0值,否则返回0;
Python compile()函数 在 Python 中,compile() 是一个内置函数,用于将字符串或AST对象编译成字节码或代码对象。编译后的字节码或代码对象可以在多个 Python 解释器中执行,从而避免每次执行时重新编译代码。
没有main()函数的C语言程序 有两种方法可以不添加main()函数来运行C语言程序,第一种用#define预处理指令,第二种是使用-nostartfiles编译选项;