前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust学习笔记Day22 何为闭包?闭包的本质是什么?

Rust学习笔记Day22 何为闭包?闭包的本质是什么?

作者头像
用户1072003
发布2023-02-23 17:01:34
6200
发布2023-02-23 17:01:34
举报
文章被收录于专栏:码上读书

我以前以为闭包就是 当前作用域的一个临时函数。作者说闭包可以方便的函数式编程。闭包

  1. 可以作为参数传递,
  2. 可以作为返回值。
  3. 可以为它实现trait。

何为闭包

作者给闭包的定义:闭包是将函数,或者说代码和其环境一起存储的一种数据结构。(闭包也是一种数据结构吗?) 闭包引用的上下文中的自由变量,会被捕获到闭包的结构中,成为闭包类型的一部分。

闭包会根据内部的使用情况,捕获环境中的自由变量。在Rust中,闭包可以用这种方式来表达

代码语言:javascript
复制
| 参数 | {
     ... 
     闭包代码实现
}

看下面的例子:

代码语言:javascript
复制
fn main() {
    let a = "Hello";
    let b = "Tyr";

    let c = |msg| {
        println!("{} {} : {}", a, b, msg);
    };

    c("How are you?");
}

上图闭包c 捕获了上下文里的a和b,然后通过引用来使用 a/b 这两个变量。

闭包还可以用 move 关键字 ,转移变量的使用权。

比如在多线程的情况下,会经常使用到: 如:

代码语言:javascript
复制

use std::thread;

fn main() {
    let s = String::from("hello world");

    let handle = thread::spawn(move || {
        println!("moved: {:?}", s);
    });

    handle.join().unwrap();
}

这是变量s的所有权就从当前作用域移动到闭包的作用域里了。

闭包的本质

闭包是一种匿名类型,一旦声明,就会产生一个新的类型,但这个类型无法被其它地方使用。这个类型就像一个结构体,会包含所有捕获的变量

所以前面说闭包是一种特殊的数据结构?

我有点迷糊,做个实验理解一下:

代码语言:javascript
复制
use std::{collections::HashMap, mem::size_of_val};
fn main() {
    // 长度为 0
    let c1 = || println!("hello world!");
    // 和参数无关,长度也为 0
    let c2 = |i: i32| println!("hello: {}", i);
    let name = String::from("tyr");
    let name1 = name.clone();
    let mut table = HashMap::new();
    table.insert("hello", "world");
    // 如果捕获一个引用,长度为 8
    let c3 = || println!("hello: {}", name);
    // 捕获移动的数据 name1(长度 24) + table(长度 48),closure 长度 72
    let c4 = move || println!("hello: {}, {:?}", name1, table);
    let name2 = name.clone();
    // 和局部变量无关,捕获了一个 String name2,closure 长度 24
    let c5 = move || {
        let x = 1;
        let name3 = String::from("lindsey");
        println!("hello: {}, {:?}, {:?}", x, name2, name3);
    };

    println!(
        "c1: {}, c2: {}, c3: {}, c4: {}, c5: {}, main: {}",
        size_of_val(&c1),
        size_of_val(&c2),
        size_of_val(&c3),
        size_of_val(&c4),
        size_of_val(&c5),
        size_of_val(&main),
    )
}

结果为

代码语言:javascript
复制
c1: 0, c2: 0, c3: 8, c4: 72, c5: 24, main: 0

这里共有5个闭包:

  • c1 没参数,没捕获变量,长度0
  • c2 有参数,类型i32, 没捕获变量,长度0。
  • c3 没参数,引用了类型为&String的变量,长度为8。
  • c4 没参数,捕获2个变量,1个String,1个HashMap,长度为72. move 转移所有权。
  • c5 无参数,捕获1个外部变量String,内部变量2个,长度24.

结论: 从 c2 可以发现,闭包大小和参数无关。从 c3 发现:不带move时,闭包捕获的变量的引用。从 c4 c5 发现,带了move后,捕获的就是变量本身了。从 c5 发现,闭包大小和局部变量无关

那和什么有关呢?只跟捕获的变量有关

Rust闭包性能好的原因

  1. 不转移所有权,会引用变量,这个引用受到借用规则的约束(只要编译通过,那么闭包对变量的引用就不会超过变量的生命周期,没有内存安全问题。)
  2. 使用move转移所有权,闭包就会接管这些变量,他们的生命周期和闭包就一样了,不会有内存安全问题。

而 Rust 为每个闭包生成一个新的类型,又使得调用闭包时可以直接和代码对应,省去了使用函数指针再转一道手的额外消耗。

Rust的设计让我们

  • 不必为堆内存管理设计 GC、
  • 不必为其它资源的回收提供 defer 关键字、
  • 不必为并发安全进行诸多限制、
  • 也不必为闭包挖空心思搞优化。

明天我们继续学习 FnOnce / FnMut / Fn 这三种闭包类型。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-02-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上读书 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 何为闭包
  • 闭包的本质
    • Rust闭包性能好的原因
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档