iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >深入了解Rust中函数与闭包的使用
  • 872
分享到

深入了解Rust中函数与闭包的使用

Rust函数闭包Rust函数Rust闭包 2022-11-13 19:11:19 872人浏览 泡泡鱼
摘要

目录闭包高阶函数发散函数闭包 Rust 的闭包由一个匿名函数加上外层的作用域组成,举个例子: fn main() {     let closure = |n: u32| ->

闭包

Rust 的闭包由一个匿名函数加上外层的作用域组成,举个例子:

fn main() {
    let closure = |n: u32| -> u32 {
        n * 2
    };
    println!("n * 2 = {}", closure(12));
    // n * 2 = 24
}

闭包可以被保存在一个变量中,然后我们注意一下它的语法,参数定义、返回值定义都和普通函数一样,但闭包使用的是两个竖线。我们对比一下两者的区别:

// 普通函数定义
fn func1(a: u32, b: u32) -> String {
    // 函数体
}

所以两者在语法上没有什么本质的区别,但这个时候可能有人好奇了,我们能不能把闭包中的匿名函数换成普通函数呢?来试一下。

fn main() {
    let closure1 = |n: u32| -> u32 {
        n * 2
    };

    fn closure2(n: u32) -> u32 {
        n * 2
    }

    println!("n * 2 = {}", closure1(12));
    println!("n * 2 = {}", closure2(12));
    
}

从表面上来看是可以的,但其实还存在问题,因为 closure2 只是一个在函数里定义的函数而已。而闭包除了要包含函数之外,还要包含函数所在的外层作用域,什么意思呢?我们举例说明:

你看到了什么?没错,在函数 closure2 内部无法使用外层作用域中的变量 a,因此它只是定义在 main 函数里的函数而已,而不是闭包,因为它不包含外层函数(main)的作用域。

而 Rust 提示我们使用 || { ... },那么 closure1 显然是闭包,因为它除了包含一个函数(匿名),还包含了外层作用域,我们将这个闭包赋值给了 closure1。

此外闭包还有一个重要的用途,就是在多线程编程时,可以将主线程的变量移动到子线程内部。

关于多线程后续会详细说,这里只是举个例子。

// 导入线程模块
use std::thread;

fn main() {
    let s = String::from("hello world");
    // 必须在 || 的前面加上 move
    // 它的含义就是将值从主线程移动到子线程
    let closure1 = move || {
        println!("{}", s);
    };
    // 开启一个子线程
    thread::spawn(closure1).join();
    
}

打印是发生在主线程当中的,而不是子线程,以上就是闭包相关的内容。

高阶函数

了解完闭包之后,再来看看高阶函数,在数学和计算机中,高阶函数是至少满足下列一个条件的函数:

  • 接收一个或多个函数作为输入;
  • 输出一个函数;

在数学中它们也叫算子或者泛函,高阶函数是函数式编程中非常重要的一个概念。

先来看看如何定义一个接收函数作为参数的函数:

// calc 接收三个参数,返回一个 i32
// 参数一:接收两个 i32 返回一个 i32 的函数
// 参数二 和 参数三均是一个 i32
fn calc(method: fn(i32, i32) -> i32,
        a: i32, b: i32) -> i32 {
    method(a, b)
}

fn add(a: i32, b: i32) -> i32 {
    a + b
}
fn main() {
    println!("a + b = {}", calc(add, 12, 33));
    

    // 也可以传递一个匿名函数,但它不能引用外层作用域的变量
    // 因为 calc 第一个参数接收的是函数,不是闭包
    let sub = |a: i32, b: i32| -> i32 {
        a - b
    };

    println!("a - b = {}", calc(sub, 12, 33));
    
}

以函数作为参数,在类型声明中我们不需要写函数名以及参数名,只需要指明参数类型、数量和返回值类型即可。

然后再观察一下函数 calc 的定义,由于第一个参数 method 接收一个函数,所以它的定义特别的长,我们能不能简化一下呢?

// 相当于给类型起了一个别名
type Method = fn(i32, i32) -> i32;

fn calc(method: Method,
        a: i32, b: i32) -> i32 {
    method(a, b)
}

这种做法也是可以的。

看完了接收函数作为参数,再来看看如何将函数作为返回值。

type Method = fn(i32, i32) -> i32;

// 想要接收字符串的话
// 应该使用引用 &String 或切片 &str
// 当然我们前面说过,更推荐切片
fn calc(op: &str) -> Method {
    fn add(a: i32, b: i32) -> i32 {
        a + b
    }
    
    let sub = |a: i32, b: i32| -> i32 { a - b };

    // 使用 if else 也是可以的
    match op {
        "add" => add,
        "sub" => sub,
        // 内置的宏,会抛出一个错误,表示方法没有实现
        _ => unimplemented!(),
    }  // 注意:此处不可以加分号,因为要作为表达式返回
}

fn main() {
    let (a, b) = (11, 33);
    println!("a + b = {}", calc("add")(a, b));
    println!("a - b = {}", calc("sub")(a, b));
    
}

以上就是高阶函数,还是很好理解的,和 python 比较类似。你可以基于这个特性,实现一个装饰器,只是 Rust 里面没有 @ 这个语法糖罢了。这里我们简单地实现一下吧,加深一遍印象。

enum Result {
    Text(String),
    Func(fn() -> String),
}

fn index() -> String {
    String::from("欢迎来到古明地觉的编程教室")
}

fn login_required(username: &str, passWord: &str) -> Result {
    if !(username == "satori" && password == "123") {
        return Result::Text(String::from("请先登录"));
    } else {
        return Result::Func(index);
    }
}

fn main() {
    let res1 = login_required("xxx", "yyy");
    let res2 = login_required("satori", "123");
    // 如果后续还要使用 res1 和 res2,那么就使用引用
    // 也就是 [&res1, &res2]
    // 但这里我们不用了,所以是 [res1, res2],此时会转移所有权
    for item in [res1, res2] {
        match item {
            Result::Text(error) => println!("{}", error),
            Result::Func(index) => println!("{}", index()),
        }
    }
    
}

是不是很有趣呢?这里再次看到了枚举类型的威力,我们有可能返回字符串,也有可能返回函数,那么应该怎么办呢?很简单,将它们放到枚举里面即可,这样它们都是枚举类型。至于到底是哪一个成员,再基于 match 分别处理即可。

还记得 match 吗?match 可以有任意多个分支,每一个分支都应该返回相同的类型,并且只有一个分支会执行成功,然后该分支的返回值会作为整个 match 表达式的返回值。

发散函数

最后再来看看发散函数,这个概念在其它语言里面应该很少听到。在 Rust 里面,发散函数永远不会返回,它的返回值被标记为 !,表示这是一个空类型。

// 发散函数的返回值类型是一个感叹号
// 它表示这个函数执行时会报错
fn foo() -> ! {
    panic!("这个函数执行时会报错")
}

fn main() {
    // 调用发散函数时,可以将其结果赋值给任意类型的变量
    let res1: u32 = foo();
    let res2: f64 = foo();
}

所以这个发散函数没啥卵用,你在实际开发中估计一辈子也用不上,因为它在执行的时候会 panic 掉。所以这段代码编译的时候是没有问题的,但执行时会触发 panic。既然执行时会报错,那么当然可以赋值给任意类型的变量。

因此当返回值类型为 ! 时,我们需要通过 panic 宏让函数在执行的过程中报错。但要注意的是,发散函数和不指定返回值的函数是不一样的,举个例子:

// 发散函数的返回值类型是一个感叹号
// 它表示这个函数执行时会报错
fn foo() -> ! {
    panic!("这个函数执行时会报错");
}

// 不指定返回值,默认返回 ()
// 所以以下等价于 fn bar() -> () {}
// 但很明显 bar 函数是有返回值的,会返回空元组
fn bar() {

}

总的来说发散函数没啥卵用,在工作中也不建议使用,只要知道有这么个东西就行。

到此这篇关于深入了解Rust中函数与闭包的使用的文章就介绍到这了,更多相关Rust函数 闭包内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 深入了解Rust中函数与闭包的使用

本文链接: https://www.lsjlt.com/news/170875.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 深入了解Rust中函数与闭包的使用
    目录闭包高阶函数发散函数闭包 Rust 的闭包由一个匿名函数加上外层的作用域组成,举个例子: fn main() {     let closure = |n: u32| ->...
    99+
    2022-11-13
    Rust 函数 闭包 Rust 函数 Rust 闭包
  • 深入了解Rust中泛型的使用
    目录楔子函数中的泛型结构体中的泛型枚举中的泛型方法中的泛型楔子 所有的编程语言都致力于将重复的任务简单化,并为此提供各种各样的工具。在 Rust 中,泛型(generics)就是这样...
    99+
    2022-11-13
    Rust泛型使用 Rust泛型
  • 深入了解Rust的切片使用
    目录为什么要有切片字符串切片其它类型的切片为什么要有切片 除了引用,Rust 还有另外一种不持有所有权的数据类型:切片(slice),切片允许我们引用集合中某一段连续的元素序列,而不...
    99+
    2022-11-13
    Rust切片使用 Rust切片
  • 深入了解Rust中引用与借用的用法
    目录楔子什么是引用可变引用悬空引用小结楔子 好久没更新 Rust 了,上一篇文章中我们介绍了 Rust 的所有权,并且最后定义了一个 get_length 函数,但调用时会导致 St...
    99+
    2022-11-13
    Rust 引用 借用 Rust 引用 Rust 借用
  • 深入了解Rust 结构体的使用
    目录楔子定义并实例化结构体简化版的实例化方式基于已有结构体实例创建元组结构体没有字段的空结构体结构体数据的所有权使用结构体的示例程序楔子 结构体是一种自定义的数据类型,它允许我们将多...
    99+
    2022-11-13
    Rust 结构体使用 Rust 结构体
  • 深入了解C++封闭类的定义与使用
    目录封闭类轮胎类引擎类汽车类总代码封闭类 今天,我学习的是C++对象的一种操作.就是成员对象和封闭类. 那么封闭类是什么呢和普通类用什么不同吗 封闭类就是有成员对象的类,那么成员对象...
    99+
    2022-11-13
    C++封闭类使用 C++封闭类
  • 深入了解python高阶函数编写与使用
    目录1.变量可以指向函数2.函数名也可以是变量。3.传入函数总结何为高阶函数,以实际代码为例子一步步深入概念。 1.变量可以指向函数 以abs()为例: >>>...
    99+
    2024-04-02
  • 深入了解Python中运算符函数的使用
    Python 在“运算符”模块下为许多数学、逻辑、关系、按位等操作预定义了函数。本文介绍了一些基本功能。 1. add(a, b)  :- 这个函数...
    99+
    2024-04-02
  • 使用工具深入了解 golang 函数
    通过 go tool objdump 命令可深入了解 go 函数的汇编代码,从而洞察其内部工作原理。例如,查看 strconv.parseint 源代码,了解其如何将字符串转换为 int...
    99+
    2024-05-06
    函数 golang
  • 深入了解Python中的os.path.join函数
    深入了解Python中的os.path.join函数 1. 引言 在Python中,处理文件和目录路径是常见的任务。为了简化路径的拼接和操作,Python提供了os.path模块,其中的join函数是...
    99+
    2023-09-08
    python 开发语言
  • 深入了解C++的多态与虚函数
    目录1.多态的机制与虚函数的机制1.1 多态的机制1.2 虚函数的机制1.3虚函数表的结构图1.4 动态多态实现的三个前提件(很重要)2.多态实例应用3.多态的巨大问题与虚析构3.1...
    99+
    2024-04-02
  • 深入了解Python中Lambda函数的用法
    目录什么是Lambda函数过滤列表中的元素和map()函数的联用和apply()方法的联用不太适合使用的场景今天来给大家推荐一个Python当中超级好用的内置函数,那便是lambda...
    99+
    2024-04-02
  • 深入理解Java中包的定义与使用
    目录包是什么?包的作用导入包中的类自定义包包的访问权限控制包是什么? 在开发过程中,会定义很多类,随着类越写越多,难免会出现类重名而发生覆盖的情况,为了在使用它们的时候不让编译器混淆...
    99+
    2024-04-02
  • 深入了解python的函数参数
    目录位置参数默认参数关键字参数多值参数:总结 位置参数 这是一个求等差数列和的函数,使用必需要传入一个参数n,这就是位置参数 def sum(n): sum=0 ...
    99+
    2024-04-02
  • 深入了解Vue3中props的原理与使用
    目录前言介绍原理前提创建组件实例对象初始化Props操作创建proxy对象去获取Propsprops作为参数传入setup将proxy挂载到render上总结前言 props指父组件...
    99+
    2023-05-19
    Vue3 props原理 Vue3 props使用 Vue3 props
  • 深入了解Android中GestureDetector的定义与使用
    目录简介赋予widget可以点击的功能会动的组件可删除的组件总结简介 之前我们介绍了GestureDetector的定义和其提供的一些基本的方法,GestureDetector的好处...
    99+
    2023-01-31
    Android GestureDetector使用 Android GestureDetector
  • JavaScript深入理解作用域链与闭包详情
    目录深入作用域链与闭包作用域链[[Environment]]完善环境记录闭包函数实例什么是闭包变量绑定同一个闭包总结深入作用域链与闭包 为什么要把作用域链和闭包放在一起讲呢,它们有什...
    99+
    2024-04-02
  • 深入了解JavaScript中的函数柯里化
    目录一、参数复用二、延迟执行三、部分应用四、函数组合JavaScript函数柯里化是一种将接受多个参数的函数转换为一系列接受单个参数的函数的技术。这种技术可以让我们更方便地创建可复用...
    99+
    2023-05-16
    JavaScript函数柯里化 JavaScript 柯里化
  • 深入了解 PHP 函数的构成
    php 函数由函数名、参数、返回类型和函数体组成。函数类型可分为标量函数、复合函数和 void 函数。标量函数返回基本值,复合函数返回复杂数据结构,void 函数不返回任何值。举例而言,...
    99+
    2024-04-11
    函数 php
  • 深入了解Go语言的基本语法与常用函数
    目录一、基本语法标识符命名规范变量的定义与使用定义常量二、常用函数main 函数与 init 函数fmt 包及其函数一、基本语法 标识符命名规范 Go 是区分大小写的,标识符的命名包...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作