学习 Rust 第 23 天:闭包

Rust 闭包提供了简洁、富有表现力的匿名函数来捕获周围的变量。它们简化了代码,提供了存储、参数传递和函数重构方面的灵活性。它们与泛型的交互增强了灵活性,而捕获模式则促进了有效的所有权和可变性管理。从本质上讲,闭包是 Rust 的基础,可以提高代码的清晰度和简洁性。

Introduction 介绍

We used a closure in themini_grep project, here’s what it looked like
我们在 mini_grep 项目中使用了闭包,如下所示

let config = Config::new(&args).unwrap_or_else(|err|{eprintln!("Problem parsing arguments: {}",err);eprintln!("Expected: {} search_query filename", args[0]);process::exit(1);});

Closures are like anonymous functions, and we can store them in variables as well, pass them as function arguments etc.
闭包就像匿名函数,我们也可以将它们存储在变量中,将它们作为函数参数传递等。

In Rust, closures are anonymous functions that can capture variables from their surrounding environment. They are similar to lambda functions or blocks in other programming languages. Closures in Rust have a flexible and powerful syntax, allowing them to be used in various contexts.
在 Rust 中,闭包是可以从周围环境捕获变量的匿名函数。它们类似于其他编程语言中的 lambda 函数或块。 Rust 中的闭包具有灵活而强大的语法,允许它们在各种上下文中使用。

fn main() {let add_one = |x| x + 1;let result = add_one(5);println!("Result: {}", result); // Output: 6
}

In this example, add_one is a closure that takes one parameter x and returns x + 1. It's assigned to a variable, and then called with an argument 5. The result is printed out.
在此示例中, add_one 是一个闭包,它采用一个参数 x 并返回 x + 1 。它被分配给一个变量,然后使用参数 5 进行调用。结果被打印出来。

This is a basic example of what a closure looks like.
这是闭包的基本示例。

Refactoring Functions to Closures
将函数重构为闭包

Refactoring functions with closures can often lead to more concise and expressive code.
使用闭包重构函数通常可以生成更简洁、更具表现力的代码。
Let’s consider a function that applies a given operation to each element in a vector:
让我们考虑一个将给定操作应用于向量中每个元素的函数:

fn apply_operation_to_vec(values: Vec<i32>, operation: fn(i32) -> i32) -> Vec<i32> {let mut result = Vec::new();for value in values {result.push(operation(value));}result
}fn double(x: i32) -> i32 {x * 2
}fn main() {let values = vec![1, 2, 3, 4, 5];let doubled_values = apply_operation_to_vec(values, double);println!("Doubled values: {:?}", doubled_values);
}

In this code, we have a function apply_operation_to_vec that takes a vector of integers and a function pointer operation representing the operation to be applied to each element of the vector.
在此代码中,我们有一个函数 apply_operation_to_vec ,它接受一个整数向量和一个表示要应用于向量每个元素的操作的函数指针 operation 。

Now, let’s refactor this function to use closures instead:
现在,让我们重构这个函数以使用闭包:

fn apply_operation_to_vec(values: Vec<i32>, operation: impl Fn(i32) -> i32) -> Vec<i32> {values.into_iter().map(operation).collect()
}fn main() {let values = vec![1, 2, 3, 4, 5];let doubled_values = apply_operation_to_vec(values, |x| x * 2);println!("Doubled values: {:?}", doubled_values);
}

In this refactored version:
在这个重构版本中:

  • We’ve changed the operation parameter to accept a closure instead of a function pointer. The impl Fn(i32) -> i32 syntax means that operation can accept any type that implements the Fn(i32) -> i32 trait, which includes closures.
    我们更改了 operation 参数以接受闭包而不是函数指针。 impl Fn(i32) -> i32 语法意味着 operation 可以接受任何实现 Fn(i32) -> i32 特征的类型,其中包括闭包。
  • Inside the apply_operation_to_vec function, we use into_iter() to consume the input vector and produce an iterator. Then, we use map() to apply the closure to each element, and collect() to collect the results into a new vector.
    在 apply_operation_to_vec 函数内,我们使用 into_iter() 来使用输入向量并生成迭代器。然后,我们使用 map() 将闭包应用于每个元素,并使用 collect() 将结果收集到新的向量中。

Both output the same data:
两者输出相同的数据:

$ cargo run
Doubled values: [2, 4, 6, 8, 10]

Type annotations 类型注释

To declare an add_one function which returns an integer, and takes an integer as a parameter. The function declaration would look something like this
声明一个返回整数并接受整数作为参数的 add_one 函数。函数声明看起来像这样

fn add_one(x: i32)->i32{x+1
}

But If I want to do the same with closures, notice how I don’t need to specify any data types here…
但是如果我想对闭包做同样的事情,请注意我不需要在这里指定任何数据类型......

fn main(){let add_one = |x| x + 1;
}

The add_one closure is defined without specifying the types of its parameters and return value. Rust's type inference mechanism automatically deduces that add_one takes an i32 parameter and returns an i32 value.
add_one 闭包的定义没有指定其参数和返回值的类型。 Rust 的类型推断机制会自动推断 add_one 接受 i32 参数并返回 i32 值。

Generic parameters and Function traits
通用参数和函数特征

Fn trait:  Fn 功能:

  • Closures that implement Fn can be called immutably.
    实现 Fn 的闭包可以被不可变地调用。
  • They capture their environment by reference, allowing them to borrow variables from the surrounding scope.
    它们通过引用捕获环境,从而允许它们从周围范围借用变量。
  • This trait is suitable for closures that don’t need to mutate the captured variables.
    此特性适用于不需要改变捕获变量的闭包。
  • Example usage: Functions that only read from the captured variables.
    用法示例:仅读取捕获变量的函数。

FnMut trait:  FnMut 功能:

  • Closures that implement FnMut can be called mutably.
    实现 FnMut 的闭包可以被可变地调用。
  • They capture their environment by mutable reference, allowing them to modify the captured variables.
    他们通过可变引用捕获环境,从而允许他们修改捕获的变量。
  • This trait is suitable for closures that need to mutate the captured variables.
    此特性适用于需要改变捕获变量的闭包。
  • Example usage: Functions that modify the captured variables but don’t consume them.
    用法示例:修改捕获的变量但不消耗它们的函数。

FnOnce trait:  FnOnce 功能:

  • Closures that implement FnOnce take ownership of the captured variables.
    实现 FnOnce 的闭包拥有捕获的变量的所有权。
  • They can only be called once because they consume the captured variables.
    它们只能被调用一次,因为它们消耗捕获的变量。
  • This trait is suitable for closures that need to consume the captured variables, transferring ownership to the closure.
    此特性适用于需要使用捕获的变量、将所有权转移给闭包的闭包。
  • Example usage: Functions that consume the captured variables, such as closures used in move semantics.
    用法示例:使用捕获变量的函数,例如移动语义中使用的闭包。

In Rust, you can use generic parameters with function traits (FnFnMutFnOnce) to make functions more flexible and reusable across different types.
在 Rust 中,您可以将泛型参数与函数特征( Fn 、 FnMut 、 FnOnce )一起使用,以使函数在不同类型之间更加灵活和可重用。

// A generic function that takes a closure and applies it to an input value
fn apply_closure<F, T>(closure: F, value: T) -> T
whereF: Fn(T) -> T,
{closure(value)
}fn main() {// Define a closure that doubles an integerlet double_closure = |x: i32| x * 2;// Apply the closure to a valuelet result = apply_closure(double_closure, 5);println!("Result: {}", result); // Output: Result: 10// Define a closure that appends a stringlet append_closure = |s: String| s + " World";// Apply the closure to a valuelet result = apply_closure(append_closure, String::from("Hello"));println!("Result: {}", result); // Output: Result: Hello World
}
  • We have a generic function apply_closure that takes two parameters: a closure (F) and a value (T). The closure must implement the Fn(T) -> T trait, meaning it takes a single parameter of type T and returns a value of type T.
    我们有一个通用函数 apply_closure ,它接受两个参数:一个闭包 ( F ) 和一个值 ( T )。闭包必须实现 Fn(T) -> T 特征,这意味着它采用 T 类型的单个参数并返回 T 类型的值。
  • Inside main(), we define two closures: double_closure and append_closure, each with different input and output types (i32 and String).
    在 main() 内部,我们定义了两个闭包: double_closure 和 append_closure ,每个闭包都有不同的输入和输出类型( i32 和 String
  • We then call apply_closure twice, passing each closure along with an appropriate value. The function applies the closure to the value and returns the result.
    然后我们调用 apply_closure 两次,传递每个闭包以及适当的值。该函数将闭包应用于该值并返回结果。

This approach allows us to use the same generic function with different closures, making our code more reusable and flexible. Additionally, by specifying the Fn trait bound, we ensure that the closures passed to apply_closure are callable and match the expected signature.
这种方法允许我们使用具有不同闭包的相同泛型函数,使我们的代码更加可重用和灵活。此外,通过指定 Fn 特征边界,我们确保传递给 apply_closure 的闭包是可调用的并且与预期签名匹配。

Capturing the environment with closures
用闭包捕捉环境

In Rust, closures can capture variables from their surrounding environment. This feature allows closures to access and manipulate variables that are defined outside of their own scope. Rust provides three ways for closures to capture variables: by reference (&T), by mutable reference (&mut T), or by value (T).
在 Rust 中,闭包可以从周围环境捕获变量。此功能允许闭包访问和操作在其自身范围之外定义的变量。 Rust 提供了三种闭包捕获变量的方法:通过引用 ( &T )、通过可变引用 ( &mut T ) 或通过值 ( T )。

Let's explore each method:
让我们探讨一下每种方法:

Capture by reference (&T):
通过引用捕获 ( &T ):

  • When a closure captures variables by reference, it borrows them immutably.
    当闭包通过引用捕获变量时,它会不可变地借用它们。
  • The closure can read but cannot modify the variables it captures.
    闭包可以读取但不能修改它捕获的变量。
  • The captured variables remain accessible and can be used after the closure’s execution.
    捕获的变量仍然可以访问,并且可以在闭包执行后使用。
  • This is the default behavior for closures that don’t explicitly specify how they capture variables.
    这是未明确指定如何捕获变量的闭包的默认行为。
fn main() {let x = 42;let closure = || {println!("Captured value: {}", x);};closure();// x is still accessible hereprintln!("Outer value: {}", x);
}

Capture by mutable reference (&mut T):
通过可变引用捕获( &mut T ):

  • When a closure captures variables by mutable reference, it borrows them mutably.
    当闭包通过可变引用捕获变量时,它会可变地借用它们。
  • The closure can read and modify the variables it captures.
    闭包可以读取和修改它捕获的变量。
  • The captured variables remain mutable after the closure’s execution.
    捕获的变量在闭包执行后仍然可变。
  • To capture variables by mutable reference, the closure must be declared with the mut keyword.
    要通过可变引用捕获变量,必须使用 mut 关键字声明闭包。
fn main() {let mut x = 42;let mut closure = || {println!("Captured value before: {}", x);x += 1;println!("Captured value after: {}", x);};closure();// x is still accessible here, and its value has been modified by the closureprintln!("Outer value: {}", x);
}

Capture by value (T):
按值捕获 ( T ):

  • When a closure captures variables by value, it takes ownership of them.
    当闭包按值捕获变量时,它就获得了它们的所有权。
  • The closure can consume the variables it captures.
    闭包可以消耗它捕获的变量。
  • After the closure’s execution, the captured variables are moved into the closure and no longer accessible in the outer scope.
    闭包执行后,捕获的变量将移至闭包中,并且无法再在外部作用域中访问。
fn main() {let x = 42;let closure = move || {println!("Captured value: {}", x);};closure();// x is not accessible here; it has been moved into the closure// println!("Outer value: {}", x); // This line would cause a compilation error
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/830736.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker部署RabbitMQ与简单使用

官网地址&#xff1a; Messaging that just works — RabbitMQ 我的Docker博客:Docker-CSDN博客 1.结构 其中包含几个概念&#xff1a; **publisher**&#xff1a;生产者&#xff0c;也就是发送消息的一方 **consumer**&#xff1a;消费者&#xff0c;也就是消费消息的一方 …

K8S 哲学 - deployment -- kubectl【create 、 rollout 、edit、scale、set】

kubectl create kubectl rollout kubectl edit kubectl set kubectl scale 1、创建与配置文件解析 2、deploy 滚动更新 &#xff1a;template 里面的内容改变触发滚动更新 编辑该 deploy 的 配置文件 &#xff0c;加入一个 label 不会触发滚动更新 改变 nginx镜…

Flutter笔记:Widgets Easier组件库(2)阴影盒子

Flutter笔记 Widgets Easier组件库&#xff08;2&#xff09;&#xff1a;阴影盒子 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…

Baidu Comate:“AI +”让软件研发更高效更安全

4月27日&#xff0c;百度副总裁陈洋出席由全国工商联主办的第64届德胜门大讲堂&#xff0c;并发表了《深化大模型技术创新与应用落地&#xff0c;护航大模型产业平稳健康发展》主题演讲。陈洋表示&#xff0c;“人工智能”成为催生新质生产力的重要引擎&#xff0c;对于企业而言…

Sentinel 控制台学习

引言 上篇文章已经讲过 SpringCloud Sentinel集成到微服务项目中&#xff0c;接下来我们继续学习怎么使用sentinel控制台对微服务进行限流&#xff0c;熔断&#xff0c;降级等一系列操作。 控制台 接下来我们单独讲解每一个菜单按钮 实时监控 实时监控&#xff1a; 可以看到…

帮助 Python 用户构建 CLI 界面:直观易写、简单高效 | 开源日报 No.240

tiangolo/typer Stars: 13.7k License: MIT typer 是一个构建出色命令行界面&#xff08;CLI&#xff09;的库&#xff0c;基于 Python 类型提示。它旨在让开发者轻松创建用户喜欢使用的 CLI 应用程序。其主要功能和核心优势包括&#xff1a; 直观易写&#xff1a;强大编辑器…

IoTDB 入门教程①——时序数据库为什么选IoTDB ?

文章目录 一、前文二、性能排行第一三、完全开源四、数据文件TsFile五、乱序数据高写入六、其他七、参考 一、前文 IoTDB入门教程——导读 关注博主的同学都知道&#xff0c;博主在物联网领域深耕多年。 时序数据库&#xff0c;博主已经用过很多&#xff0c;从最早的InfluxDB&a…

简单数据加解密,JS和JAVA同时实现

前端Vue调用Java后端接口中的数据进行加密&#xff0c;以避免敏感数据泄露。 现在实现一个高性能加密方法&#xff0c;用来对数据进行加密后传输。算法包括JS的加密和解密方法&#xff0c;也包括Java的加密解密方法。 可以在前端加密&#xff0c;后端解密。也可以在后端加密&…

【学习vue 3.x】(二)组件应用及单文件组件

文章目录 章节介绍本章学习目标学习前的准备工作Vue.js文件下载地址 组件的概念及组件的基本使用方式组件的概念组件的命名方式与规范根组件局部组件与全局组件 组件之间是如何进行互相通信的父子通信父子通信需要注意的点 组件的属性与事件是如何进行处理的组件的属性与事件 组…

网盘—下载文件

本文主要讲解网盘文件操作的下载文件部分&#xff0c;具体步骤如下&#xff1a; 目录 1、实施步骤 2、代码实现 2.1、添加下载文件的协议 2.2、添加下载文件函数 2.3、添加信号槽 2.4、实现槽函数 2.5、设置download状态 2.6、添加定义 2.7、服务器接收数据 2.8、添…

计算机英文论文常见错误写作习惯2

目录 第一部分 非常长的句子 在一个句子的主要概念的前面&#xff0c;首先说明目的、地点或原因 将表示时间的短语放在句首的倾向 将最重要的主语放在句首&#xff0c;以示强调 ‘In this paper’, ‘in this study’ 第一部分 非常长的句子 由于作者经常直接从中文翻译…

【云原生】Docker 实践(三):使用 Dockerfile 文件构建镜像

Docker 实践&#xff08;三&#xff09;&#xff1a;使用 Dockerfile 文件构建镜像 1.使用 Dockerfile 文件构建镜像2.Dockerfile 文件详解 1.使用 Dockerfile 文件构建镜像 Dockerfile 是一个文本文件&#xff0c;其中包含了一条条的指令&#xff0c;每一条指令都用于构建镜像…

VSCode SSH连接远程主机失败,显示Server status check failed - waiting and retrying

vscode ssh连接远程主机突然连接不上了&#xff0c;终端中显示&#xff1a;Server status check failed - waiting and retrying 但是我用Xshell都可以连接成功&#xff0c;所以不是远程主机的问题&#xff0c;问题出在本地vscode&#xff1b; 现象一&#xff1a; 不停地输入…

39.WEB渗透测试-信息收集-域名、指纹收集(1)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;38.WEB渗透测试-信息收集-信息收集-企业信息收集&#xff08;5&#xff09; 子域名信息收…

ZooKeeper 搭建详细步骤之一(单机模式)

ZooKeeper 搭建详细步骤之三&#xff08;真集群&#xff09; ZooKeeper 搭建详细步骤之二&#xff08;伪集群模式&#xff09; ZooKeeper 搭建详细步骤之一&#xff08;单机模式&#xff09; ZooKeeper 及相关概念简介 搭建模式简述 ZooKeeper 的搭建模式包括单机模式、集群模…

YOLOv8核心原理深度解析

YOLOv8源码地址: https://github.com/ultralytics/ultralytics 一、简介: 根据官方描述,Yolov8是一个SOTA模型,它建立在Yolo系列历史版本的基础上,并引入了新的功能和改进点,以进一步提升性能和灵活性,使其成为实现目标检测、图像分割、姿态估计等任务的最佳选择。其具体…

AI图书推荐:将 ChatGPT和Excel融合倍增工作效率

《将 ChatGPT和Excel融合倍增工作效率》&#xff08; Hands-on ChatGPT in Excel. Enhance Your Excel Workbooks&#xff09;由Mitja Martini撰写&#xff0c;旨在教授读者如何将ChatGPT与Excel结合使用&#xff0c;以提升工作效率和创造AI增强的Excel工具。它还提供了Excel中…

AnomalyGPT——使用大型视觉语言模型进行工业异常检测的算法解析与应用

1.概述 工业缺陷检测是工业自动化和质量控制中的一个重要环节&#xff0c;其目的是在生产过程中识别和分类产品或组件中的缺陷&#xff0c;以确保最终产品的质量满足既定标准。这项技术的应用可以显著提高生产效率&#xff0c;降低成本&#xff0c;并减少由于缺陷产品导致的潜…

sgg_ssm学习--前端搭建遇到的问题

目录 问题1&#xff1a;由于我是解压缩软件nodejs&#xff0c;没有添加系统路径 解决&#xff1a;添加nodejs的路径 到系统 path中 问题2&#xff1a;vscode 终端输入npm命令 报错 解决(如图所示在vscode打开前端工程&#xff0c;终端修改如下配置)&#xff1a; 问题1&…

如何在iPhone上恢复出厂设置后恢复数据

你不想让这种情况发生&#xff0c;但它确实发生了。您必须将iPhone恢复出厂设置。当您的 iPhone 上出现软件问题且无法修复时&#xff0c;可能会发生这种情况。相反&#xff0c;在更新期间&#xff0c;或者您的iPhone遇到问题时&#xff0c;iPhone上的数据不再存在。 不过不用…