学习 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.rhkb.cn/news/317011.html

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

相关文章

c#数据库: 9.删除和添加新字段/数据更新

先把原来数据表的sexy字段删除,然后重新在添加字段sexy,如果添加成功,sexy列的随机内容会更新.原数据表如下: using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Linq; using System.…

安全免费的远程软件有哪些?

远程访问软件&#xff0c;又称远程协助软件或远程控制软件&#xff0c;正在迅速走红。这类软件无论您身处何地&#xff0c;都能轻松实现远程访问和计算机控制。对于个人而言&#xff0c;远程控制工具使工作更加灵活、便捷&#xff1b;而对企业而言&#xff0c;远程访问软件也是…

jenkins转载文本

基于Docker容器DevOps应用方案 企业业务代码发布系统 一、企业业务代码发布方式 1.1 传统方式 以物理机或虚拟机为颗粒度部署部署环境比较复杂&#xff0c;需要有先进的自动化运维手段出现问题后重新部署成本大&#xff0c;一般采用集群方式部署部署后以静态方式展现 1.2 容…

【linuxC语言】进程概念与fork

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、进程的概念二、进程基本函数2.1 fork函数2.2 getpid与getppid函数 三、示例代码总结 前言 在 Linux 系统编程中&#xff0c;进程是计算机中正在执行的程序…

ip ssl证书无限端口网站

IP SSL证书是由CA认证机构颁发的一种特殊数字证书。大部分SSL数字证书都需要用户使用域名进行申请&#xff0c;想要对公网IP地址加密实现https访问就需要申请IP SSL证书。IP SSL证书采用了强大的加密算法&#xff0c;可以有效地防止数据在传输过程中被窃取或篡改&#xff0c;具…

使用 Python 和 DirectShow 从相机捕获图像

在 Python 中使用 OpenCV 是视觉应用程序原型的一个非常好的解决方案,它允许您快速起草和测试算法。处理从文件中读取的图像非常容易,如果要处理从相机捕获的图像,则不那么容易。OpenCV 提供了一些基本方法来访问链接到 PC 的相机(通过对象),但大多数时候,即使对于简单的…

html表格导出为word文档,导出的部分表格内无法填写文字

导出技术实现&#xff1a;fileSaver.jshtml-docx-js 1.npm安装 npm install --save html-docx-js npm install --save file-saver 2.页面引入 import htmlDocx from html-docx-js/dist/html-docx; import saveAs from file-saver;components: {htmlDocx,saverFile, }, 3.页…

【Paddle】PCA线性代数基础 + 领域应用:人脸识别算法(1.1w字超详细:附公式、代码)

【Paddle】PCA线性代数基础及领域应用 写在最前面一、PCA线性代数基础1. PCA的算法原理2. PCA的线性代数基础2.1 标准差 Standard Deviation2.2 方差 Variance2.3 协方差 Covariance2.4 协方差矩阵 The Covariance Matrix2.5 paddle代码demo①&#xff1a;计算协方差矩阵2.6 特…

【记录】Python3| 将 PDF 转换成 HTML/XML(✅⭐⭐⭐⭐pdf2htmlEX)

本文将会被汇总至 【记录】Python3&#xff5c;2024年 PDF 转 XML 或 HTML 的第三方库的使用方式、测评过程以及对比结果&#xff08;汇总&#xff09;&#xff0c;更多其他工具请访问该文章查看。 文章目录 pdf2htmlEX 使用体验与评估1 安装指南2 测试代码3 测试结果3.1 转 HT…

美国言语听力学会(ASHA)关于非处方 (OTC) 助听器的媒体声明(翻译稿)

美国国会于 2021 年 4 月 13 日批准美国听力学会积极提供建议&#xff0c;并一直积极参与制定FDA关于非处方助听器销售的拟议法规。根据2017年通过的立法授权。学院积极参与帮助塑造授权立法&#xff0c;并就即将出台的条例分享了建议。 根据美国卫生与公众服务部NIH / NIDCD的…

Flink checkpoint 源码分析- Checkpoint barrier 传递源码分析

背景 在上一篇的博客里&#xff0c;大致介绍了flink checkpoint中的触发的大体流程&#xff0c;现在介绍一下触发之后下游的算子是如何做snapshot。 上一篇的文章: Flink checkpoint 源码分析- Flink Checkpoint 触发流程分析-CSDN博客 代码分析 1. 在SubtaskCheckpointCoo…

数据挖掘之基于Lightgbm等多模型消融实验的信用欺诈检测实现

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 在当前的金融环境中&#xff0c;信用欺诈行为日益增多&#xff0c;给金融机构和消费者带来了巨大的损…

深入理解正则表达式:从入门到精通

title: 深入理解正则表达式&#xff1a;从入门到精通 date: 2024/4/30 18:37:21 updated: 2024/4/30 18:37:21 tags: 正则Python文本分析日志挖掘数据清洗模式匹配工具推荐 第一章&#xff1a;正则表达式入门 介绍正则表达式的基本概念和语法 正则表达式是一种用于描述字符串…

Rundeck(一)安装与启动

目录 自动化运维工具rundeck 它能做什么 系统环境 下载与安装 主要的目录&#xff08;RPM&#xff09; 配置与启动&#xff08;单节点&#xff09; 配置mysql mysql初始化 配置URL 启动rundeck 指定java版本 启动与开机启动 简单的配置一个nginx 代理 登录 升级 …

设计模式第二次测试 | 数据库连接池设计(原型模式、创建者模式、适配器模式)

需求中文如下&#xff1a;原本是英文&#xff0c;用百度翻译转换而来 我们需要设计一个工具&#xff0c;它负责创建一个与数据库软件MySQL的连接池。 连接池中有数百个连接可供客户端使用。 所有连接对象都有相同的内容&#xff0c;但它们是不同的对象。 连接对象的创建是资源密…

华为5700配置

恢复出厂设置&#xff0c;清空配置 1、更改名字 system-view sysname tp-10-50-01-04 2、配置管理接口 int vlan 1 ip add 10.50.1.4 255.255.254.0 quit 2、链路汇聚 interface eth-trunk 1 mode lacp quit 3、绑定端口 interface eth-trunk 1 trunkport gigabitethernet …

如何删除BigKey1

方案一 拆分为string类型 keyvalueid:0value0..........id:999999value999999 存在的问题&#xff1a; string结构底层没有太多内存优化&#xff0c;内存占用较多 想要批量获取这些数据比较麻烦 方案二 拆分为小的hash&#xff0c;将 id / 100 作为key&#xff0c; 将id …

uniApp+Vue3+vite+Element UI或者Element Plus开发学习,使用vite构建管理项目,HBuilderX做为开发者工具

我们通常给小程序或者app开发后台时&#xff0c;不可避免的要用到可视化的数据管理后台&#xff0c;而vue和Element是我们目前比较主流的开发管理后台的主流搭配。所以今天石头哥就带大家来一起学习下vue3和Element plus的开发。 准备工作 1&#xff0c;下载HBuilderX 开发者…

ubuntu 24.04 beta server NAT模式上网设置

在Ubuntu 24.04 Beta上设置网络通常涉及使用命令行工具。以下是设置静态IP地址和动态IP地址的步骤&#xff1a; 动态IP设置&#xff1a; 查找你的网络接口名称&#xff1a; ip a ens37是我NAT模型的一张网卡&#xff0c;此时是没有ip的。 下面介绍如何NAT模式下添加DHCP动态…

Ajax.

目录 1. 服务器相关的基础概念 1.1 服务器 1.2 客户端 1.3 服务器对外提供的资源 1.4 数据也是资源 1.5 资源与 URL 地址 1.6 什么是 Ajax 2. Ajax 的基础用法 2.1 POST 请求 2.2 GET 请求 2.3 DELETE 请求 2.4 PUT 请求 2.5 PATCH 请求 3. axios 3.1 axios 的基…