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

【Rust自学】15.1. 使用Box<T>智能指针来指向堆内存上的数据

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

15.1.1. Box<T>

box<T>可以被简单地理解为装箱,它是最简单的智能指针,允许你在堆内存上存储数据(而不是栈内存)。

具体的实现方式是Box<T>在栈内存上有一小块内存,存放指针,指向它存在堆内存上的数据。也就是说,实际的数据是存储在堆内存上的。除了它把数据存在堆内存上之外,就没有其它开销了,代价就是没有其它额外的功能。

这样看Box<T>跟普通指针好像没什么区别,但其真正的不同是Box<T>实现了DerefDrop这两个trait。

15.1.2. Box<T>的常见场景

在编译时,某类型的大小无法确定。但使用该类型时,上下文却需要知道它确切的大小,这个时候就可以选用Box<T>

当你有大量数据,想移交所有权,但需要确保在操作时不会被复制。

使用某个值时,你只关心它是否实现了特定的trait,而不关心的具体类型。

15.1.3. 使用Box<T>在堆内存上存储数据

看个例子:

fn main() {
    let b = Box::new(5);
    println!("b = {b}");
}

我们将变量b定义为具有指向值5的Box的值,该值分配在堆上。该程序将打印b = 5

和其他任何拥有所有权的值一样,b这个变量离开作用域的时候(也就是第4行花括号结束的时候),会和其他任何拥有所有权的变量一样释放内存(堆上的和栈上的都会被释放)。

15.1.4. 使用Box赋能递归类型

在编译时,Rust需要知道一个类型所占的空间大小。但是有一种被称为递归的类型,它的大小无法在编译时确定。
请添加图片描述

以这个图为例,Cons类型里面有两个字段,一个字段是i32,另一个字段是这个字段Cons本身的类型。

在编译时,Rust需要知道它的大小,i32大小是固定的,但是Cons的第二个字段Cons本身的类型大小无法确定。

针对这种情况,可以使用Box。针对递归类型,Box有办法确定其大小。

这种东西在函数式语言中是存在的,叫做Cons List

15.1.5. 关于Cons List

Cons List是来自Lisp语言的一种数据结构,这种数据结构里每个成员由两个元素组成,一个是当前项的值,比如上图中的i32;另一个是下一个元素。

这种数据结构就这样一直递归下去直到最后一个元素(最后一个成员),它里面只包含一个Nil值,没有下一个元素了,而Nil值就相当于是一个终止的标记。

NilNone的概念不一样,None表示的是无效或缺失的值,而Nil是一个终止的标记。

Cons List由上图就可以看出是一种链表。

15.1.6. Cons List在Rust中的替代者

Cons List并不是Rust中的常用集合。通常情况下,Vec<T>是更好的选择。

下面用Vec<T>创建一个同上图结构相同的Cons List

enum List {
    Cons(i32, List),
    Nil,
}

List这个枚举类型有两个变体:一个Cons一个NilCons变体附带了两个数据,一个是i32类型,一个是List类型。

这么写逻辑上没问题,但运行时会报错:

$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
error[E0072]: recursive type `List` has infinite size
 --> src/main.rs:1:1
  |
1 | enum List {
  | ^^^^^^^^^
2 |     Cons(i32, List),
  |               ---- recursive without indirection
  |
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
  |
2 |     Cons(i32, Box<List>),
  |               ++++    +

error[E0391]: cycle detected when computing when `List` needs drop
 --> src/main.rs:1:1
  |
1 | enum List {
  | ^^^^^^^^^
  |
  = note: ...which immediately requires computing when `List` needs drop again
  = note: cycle used when computing whether `List` needs drop
  = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

Some errors have detailed explanations: E0072, E0391.
For more information about an error, try `rustc --explain E0072`.
error: could not compile `cons-list` (bin "cons-list") due to 2 previous errors

因为Rust需要知道类型所占的空间大小,但递归类型的大小Rust无法计算。

15.1.7. Rust计算类型所占空间大小的方法

先看看Rust是如何计算出类型所占的空间大小的。举个例子:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

为了确定为Message值分配多少空间,Rust 会遍历每个变体以查看哪个变体需要最多空间。

Rust 认为Message::Quit不需要任何空间, Message::Move需要足够的空间来存储两个i32值,依此类推。因为每个时刻只有一种变体存在,因此Message值所需的最大空间就是存储其最大变体所需的空间,也就是ChangeColor这个变体。

15.1.8. 使用Box<T>来获得确定大小的递归类型

刚才讲了,Rust需要知道类型所占的空间大小,但递归类型的大小Rust无法计算。那么只要使用确定大小的类型就可以了,而Box<T>正好满足需求:它不存储数据,而是存储指向数据的指针,指针的大小是固定的usize

Rust知道Box<T>的大小是因为Box<T>本质上是一个指针,指针不直接存储值,所以不论指针指向的数据如何变指针本身的大小都不会变。也就是说,指针的大小不会基于它指向的数据的大小变化而变化。

针对这点就可以对原来的代码进行修改了。具体来说,把大小不确定的部分,也就是嵌套的List类型改成Box<List>类型:

enum List {
    Cons(i32, Box<List>),
    Nil,
}

这仍旧是递归,但是不会直接存储List类型,而是以间接的方式指向堆内存中List的位置,属于是曲线救国。

15.1.9. Box类型的特性总结

  • 只提供了“间接”存储和堆内存分配的功能
  • 没有额外功能
  • 没有性能开销
  • 适用于需要“间接”存储的场景,例如Cons List
  • 实现了DerefDrop trait

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

相关文章:

  • 01-硬件入门学习/嵌入式教程-CH340C使用教程
  • freeswitch在centos上编译过程
  • ESP32服务器和PC客户端的Wi-Fi通信
  • 基于Springboot用axiospost请求接收字符串参数为null的解决方案
  • sqlite3 学习笔记
  • BLE透传方案,IoT短距无线通信的“中坚力量”
  • docker入门——多用户服务器管理(小白)
  • 实战网络安全:渗透测试与防御指南
  • 汽车行业敏捷转型的推动者:ScrumCN的优势与实践
  • GESP2024年3月认证C++六级( 第三部分编程题(1)游戏)
  • 【ES实战】治理项之索引模板相关治理
  • React 前端框架实战教程
  • skynet 源码阅读 -- 「揭秘 Skynet 网络通讯」
  • C语言I/O请使用互斥锁和信号量分别实现5个线程之间的同步
  • java求职学习day17
  • 1.26学习
  • 2025年01月26日Github流行趋势
  • Python3 【正则表达式】:经典示例参考手册
  • 寒假1.25
  • 第04章 15 vtkObjectBase和vtkObject的基本特性及它们在VTK类体系中基础性作用
  • 动手学图神经网络(4):利用图神经网络进行图分类
  • 云岚到家项目100问 v1.0
  • 二叉树高频题目——下——不含树型dp
  • 基于单片机的智能小区门禁系统设计(论文+源码)
  • 【填充——双指针,DP】
  • 【算法】剪枝与优化