【Rust自学】19.3. 高级函数和闭包

news/2025/2/4 13:29:28 标签: rust, 开发语言, 后端

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
请添加图片描述

19.3.1. 函数指针(function pointer)

我们之前讲过如何把闭包传给函数,而实际上我们还可以把函数传递给函数。

在传递过程中,函数会被强制转换为fn类型,也就是函数指针(function pointer)

看个例子:

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

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(add_one, 5);

    println!("The answer is: {answer}");
}

do_twice的第一个参数ffn类型的,也就是一个函数指针。它要求作为参数的这个函数的参数是i32,返回值也是i32。在函数体里两次调用了f

输出:

The answer is 12

函数指针与闭包的不同

闭包最起码实现了FnFnOnceFnMut这三个trait之一,而函数指针fn是一个类型而不是trait。我们可以直接指定fn为参数类型,不用生命一个以Fn trait为约束的泛型参数。

函数指针实现了全部3种闭包trait,也就是FnFnOnceFnMut这三个trait。所以总是可以把函数指针用作参数传递给一个接收闭包的函数。正是因为这个原因,我们倾向于搭配闭包trait的泛型来编写函数,这样可以同时接收闭包和普通函数。


而在某些情况下,我们可能想要接收fn类型而不是闭包,比如说与外部不支持闭包的代码(C函数)交互,我们该怎么写呢?

看例子:

rust">fn main(){
    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> = list_of_numbers
    .iter()
    .map(|i| i.to_string())
    .collect();
	// 分行写只是为了好看,不是必要
}

list_of_numbers这个Vector里的元素是i32类型,而后文我们想要把list_of_numbers的元素转换为String类型赋给list_of_strings。具体做法就是:

  • 先使用iter方法产生迭代器
  • 然后使用map内的闭包|i| i.to_string()对每一个元素进行转换
  • 最后使用collect方法把每个元素合在一起转换为集合

这个代码也可以这么写:

rust">fn main(){
    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> = list_of_numbers
    .iter()
    .map(ToString::to_string)
    .collect();
}

不同之处就在于.map(ToString::to_string),这里是直接把to_string这个函数传进去了。这样写跟之前那么写的效果是一样的。顺带一提,ToString::to_string使用了前一篇文章所说的完全限定语法的知识。

我们看看map方法的定义:

rust">fn map<B, F>(self, f:F) -> Map<Self, F>
where
	Self: Sized
	F: FnMut(self::Item) -> B

map要求f实现FnMut这个trait,而闭包和函数指针都满足这一条件所以可以往里面传。

再看一个例子:

rust">fn main(){
    enum Status {
        Value(u32),
        Stop,
    }

    let list_of_statuses: Vec<Status> = (0u32..20)
    .map(Status::Value)
    .collect();
}

注意看map方法的参数,我们使用Status::Value的初始化函数调用map范围内的每个u32值来创建Status::Value实例。

有人可能会问了Status::Value不是一个枚举类型变体吗?怎么又变成了函数呢?这是因为在Rust中这样的构造器被实现为了函数,它会接收一个参数并返回一个新的实例。也就是说:

rust">let v = Status::value(3);

这是我随便举的例子,这里面的v被初始化了,而Status::value(3)就可以被看作是一个构造器,3是构造器的参数。而构造器又被实现为了函数,所以可以把它理解为函数,而3是其参数。

所以我们可以把这样的构造器也作为实现了闭包trait的函数指针来进行使用。

19.3.2. 返回闭包

闭包使用trait进行表达,无法在函数中直接返回一个闭包,可以将一个实现了该trait的具体类型作为返回值。

看个例子:

rust">fn returns_closure() -> dyn Fn(i32) -> i32 {
    |x| x + 1
}

这个函数想要直接返回闭包。

输出:

error[E0746]: return type cannot have an unboxed trait object
 --> src/lib.rs:1:25
  |
1 | fn returns_closure() -> dyn Fn(i32) -> i32 {
  |                         ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
help: consider returning an `impl Trait` instead of a `dyn Trait`
  |
1 | fn returns_closure() -> impl Fn(i32) -> i32 {
  |                         ~~~~
help: alternatively, box the return type, and wrap all of the returned values in `Box::new`
  |
1 ~ fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
2 ~     Box::new(|x| x + 1)
  |

For more information about this error, try `rustc --explain E0746`.
error: could not compile `functions-example` (lib) due to 1 previous error

Rust不知道需要多少空间来存储闭包,所以会报错。

还记得我们还在哪里遇到过Rust不知道需要多少空间来存储这个错误吗?没错,在链表时也遇到过,当时的做法是使用Box<T>包裹链表,这里也可以这么写:

rust">fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

由于返回值是一个指针,所以这个时候返回类型就有固定的大小(usize)了。


http://www.niftyadmin.cn/n/5841592.html

相关文章

k8s二进制集群之ETCD集群证书生成

安装cfssl工具配置CA证书请求文件创建CA证书创建CA证书策略配置etcd证书请求文件生成etcd证书 继续上一篇文章《负载均衡器高可用部署》下面介绍一下etcd证书生成配置。其中涉及到的ip地址和证书基本信息请替换成你自己的信息。 安装cfssl工具 下载cfssl安装包 https://github…

html中的表格属性以及合并操作

表格用table定义&#xff0c;标签标题用caption标签定义&#xff1b;用tr定义表格的若干行&#xff1b;用td定义若干个单元格&#xff1b;&#xff08;当单元格是表头时&#xff0c;用th标签定义&#xff09;&#xff08;th标签会略粗于td标签&#xff09; table的整体外观取决…

IntelliJ IDEA远程开发代理远程服务器端口(免费内网穿透)

IntelliJ IDEA远程开发代理远程服务器端口&#xff08;免费内网穿透&#xff09;&#xff08;JetBrains家的其他IDE应该也支持&#xff09; 之前看到宇宙第一IDE VS Code好像默认代理了远程的端口&#xff0c;但是一直没找到IDEA的同类功能&#xff0c;这次终于发现了 以Intell…

解决对axios请求返回对象进行json化时报“TypeError Converting circular structure to JSON“错误的问题

发现直接对axios请求返回对象进行json化会报"TypeError: Converting circular structure to JSON"错误&#xff0c;而对返回对象下的data属性进行json化就没问题 如果想对循环引用的对象进行json化&#xff0c;解决方案可参考&#xff1a; TypeError: Converting c…

机器学习--1.KNN机器学习入门

1、机器学习概述 1.1、什么是机器学习 机器学习&#xff08;Machine Learning&#xff09;是人工智能&#xff08;Artificial Intelligence&#xff09;领域的一个子集&#xff0c;它主要关注如何让计算机系统通过经验学习&#xff08;数据&#xff09;并自动改进性能。机器学…

【LeetCode 刷题】回溯算法(5)-棋盘问题

此博客为《代码随想录》二叉树章节的学习笔记&#xff0c;主要内容为回溯算法棋盘问题相关的题目解析。 文章目录 51. N皇后37. 解数独332.重新安排行程 51. N皇后 题目链接 class Solution:def solveNQueens(self, n: int) -> List[List[str]]:board [[. for _ in rang…

Javascript代码库-jQuery入门

摘自千锋教育kerwin的js教程 jQuery 是一个前端库&#xff0c;也是一个方法库他里面封装着一些列的方法供我们使用我们常用的一些方法它里面都有&#xff0c;我们可以直接拿来使用就行了jQuery 之所以好用&#xff0c;很多人愿意使用&#xff0c;是因为他的几个优点太强大了 优…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.24 随机宇宙:生成现实世界数据的艺术

1.24 随机宇宙&#xff1a;生成现实世界数据的艺术 目录 #mermaid-svg-vN1An9qZ6t4JUcGa {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-vN1An9qZ6t4JUcGa .error-icon{fill:#552222;}#mermaid-svg-vN1An9qZ6t4JUc…