当前位置: 首页 > article >正文

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

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

19.3.1. 函数指针(function pointer)

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

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

看个例子:

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函数)交互,我们该怎么写呢?

看例子:

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方法把每个元素合在一起转换为集合

这个代码也可以这么写:

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方法的定义:

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

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

再看一个例子:

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中这样的构造器被实现为了函数,它会接收一个参数并返回一个新的实例。也就是说:

let v = Status::value(3);

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

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

19.3.2. 返回闭包

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

看个例子:

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>包裹链表,这里也可以这么写:

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

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


http://www.kler.cn/a/530142.html

相关文章:

  • 【llm对话系统】大模型 Llama 源码分析之 LoRA 微调
  • 【xdoj-离散线上练习】T251(C++)
  • 【C语言】结构体对齐规则
  • Vue 3 30天精进之旅:Day 12 - 异步操作
  • 解决whisper 本地运行时GPU 利用率不高的问题
  • Dijkstra算法解析
  • 【TCP协议】流量控制 滑动窗口 拥塞控制
  • 第二篇:多模态技术突破——DeepSeek如何重构AI的感知与认知边界
  • Spring应用场景 特性
  • 【C语言】自定义类型讲解
  • mysql字段名批量大小写转换
  • HarmonyOS NEXT:保存应用数据
  • 消息队列应用示例MessageQueues-STM32CubeMX-FreeRTOS《嵌入式系统设计》P343-P347
  • vector容器(详解)
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.3 结构化索引:记录数组与字段访问
  • ExternalName Service 针对的是k8s集群外部有api服务的场景?
  • Haskell语言的多线程编程
  • [权限提升] Windows 提权 维持 — 系统错误配置提权 - Trusted Service Paths 提权
  • IM 即时通讯系统-43-简单的仿QQ聊天安卓APP
  • 2024 年 6 月大学英语四级考试真题(第 3 套)——纯享题目版
  • linux本地部署deepseek-R1模型
  • 内部知识库助力组织智力激发与信息共享实现业绩增长
  • 《手札·开源篇》从开源到商业化:中小企业的低成本数字化转型路径 ——以Odoo为数据中台低成本实现售前售中一体化
  • [Java]异常
  • GAMES101学习笔记(六):Geometry 几何(基本表示方法、曲线与曲面、网格处理)
  • 海外问卷调查渠道查,如何影响企业的运营