【rust中的闭包】

Rust中的闭包


什么是闭包

简而言之,闭包就是可以直接访问其外部作用域的函数内部匿名函数(有点狭隘,不过大部分如此)。


c++中的匿名函数:

rust 复制代码
#include <iostream>
#include <functional>
 
int main() {
	int a = 7;
    auto add = [a]( int b) {//这里
        return a + b;
    };
    std::cout << add(4) << std::endl;
 
    return 0;
}

可以看到,c++中使用lambda表达式写出的匿名函数需要开发者手动捕获所在作用域中变量的值,

回顾一些C++中的回调函数:

主要作为函数中的参数使用,既函数指针,一般使用时比如作为sort函数的比较函数,for_each中的回调等.

Rust中的闭包:

rust 复制代码
1 fn main() {
2    let a: i32 = 10;
3    let closure = |b| {a + b};
4    let closure = |b| a + b; //简单写法

    println!("{}", closure(2));
    println!("{}", closure(3));
//    println!("{}", closure(1.0));error
}

有人看到第4行代码会疑惑这不是分号结尾返回了()吗,为什么会有返回值? 其实这里只是省略了大括号的简单写法,推荐习惯性使用大括号,避免在代码较多的情况下看漏.

有上述代码可见1. rust中的闭包可以直接捕获环境中的变量 .2. 闭包可以类型推导3.编译器类型推导过一次,便已确定,不可以再次推导成其他类型.

rust闭包中的三种FN特征:

  1. FnOnce applies to closures that can be called once. All closures implement at least this trait, because all closures can be called. A closure that moves captured values out of its body will only implement FnOnce and none of the other Fn traits, because it can only be called once.
    只实现fnonce的闭包只能调用一次,它转移了所有权
  2. FnMut applies to closures that don't move captured values out of their body, but that might mutate the captured values. These closures can be called more than once.
    FnMut实现了可变借用,可以调用多次.
  3. Fn applies to closures that don't move captured values out of their body and that don't mutate captured values, as well as closures that capture nothing from their environment. These closures can be called more than once without mutating their environment, which is important in cases such as calling a closure multiple times concurrently
    Fn 不可变借用,并发多次调用闭包时非常有用.

代码示例:

rust 复制代码
fn test1<F>(closure: F)
where
    F: FnOnce(usize) -> bool,
{
    println!("{}", closure(10));//执行一次,x所有权已经被夺取并再走出闭包作用域后失效
    println!("{}", closure(11));//error: 使用了已经被move的值.
}

fn myFn<F: Fn(String) -> ()>(f: F) {
    f("world".to_string())
}

fn main() {
    let x = String::from("hello world");
    test1(|z| z == x.len());
    let mut len_after_append = |str| {
        y.push_str(str);
        y.len()
    }; //FnMut,需要给闭包变量加mut修饰,既可变的.
    println!("{}", len_after_append(" coder"));

	let cc = String::from("hello world");

    let update_string = |w| println!("{}-{}", cc, w);

    exec(update_string);//Fn特征闭包调用
}

move闭包with Spawn

使用move闭包可以强制获得变量所有权,这在多线程中spawn运行大于当前函数运行时间的时候非常好用.

rust 复制代码
use std::thread::{self, sleep};
use std::time;

fn test() {
    let list = vec![1, 2, 3];
    println!("Before defining closure: {list:?}");

    thread::spawn(move || {
        println!("From thread: {list:?}");
        sleep(time::Duration::from_millis(1000));//模拟运行时长,在长IO下,也尽量使用多线程而不是异步
        //,否则会有单任务阻塞情况.
    });
    println!("completely test");
}
fn main() {
    test();
    println!("completely main");
}

打印结果:

rust 复制代码
Before defining closure: [1, 2, 3]
completely test
completely main
From thread: [1, 2, 3]

源代码来看:

rust 复制代码
pub trait Fn<Args: Tuple>: FnMut<Args> {
    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
pub trait FnMut<Args: Tuple>: FnOnce<Args> {
    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}

可以看到的是, Fn 和 FnMut 都是 FnOnce 的子特征,因此可以在需要 FnOnce 的地方使用
Fn 或 FnMut的任何实例。


总结

如果你只想调用一次,用Fnonce,如果你想改变用FnMut,如果你想在多线程中使用且不改变用Fn.

"我们都无法确定选择是否正确,只是努力把选择变得正确."

相关推荐
2501_941875281 小时前
从配置中心到动态管理的互联网工程语法演进与多语言实践分享
开发语言·python
hfywmsj1 小时前
广州餐饮铺位招租决策模型:多因子选址系统设计
开发语言·人工智能·python·广州餐饮铺位招租
vx-Biye_Design1 小时前
SSM伴侣动物伴护星小程序06330-计算机课程设计、毕业设计
spring boot·后端·elasticsearch·小程序·架构·课程设计·idea
努力的lpp3 小时前
php反序列化一(大白话版)
开发语言·web安全·php·ctf·反序列化
2分钟速写快排3 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
菜鸟~noob2333 小时前
【电子战】第03篇:CFAR(恒虚警)处理【含matlab代码】
开发语言·matlab
面向Google编程3 小时前
Apache Iceberg Variant 类型:v3 半结构化数据不再「二选一」
大数据·后端
jason成都4 小时前
Spring WebFlux 适配达梦新方案|dm‑r2dbc:Netty 异步传输的实验性 R2DBC 驱动
java·后端·spring
泡海椒4 小时前
内置SPI函数库详解:JQuick-Java Builtin工具类实战用法
java·开发语言·python
hqyjzsb4 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析