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

news2025/1/31 1:32:08

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

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.coloradmin.cn/o/2286689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hive:复杂数据类型之Map函数

Map函数 是Hive里面的一种复杂数据类型, 用于存储键值对集合。Map中的键和值可以是基础类型或复合类型&#xff0c;这使得Map在处理需要关联存储信息的数据时非常有用。 定义map时,需声明2个属性: key 和 value , map中是 key value 组成一个元素 key-value, key必须为原始类…

Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测(附模型研究报告)

Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测&#xff08;附模型研究报告&#xff09; 目录 Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测&#xff08;附模型研究报告&#xff09;分类效果基本描述程序设…

Midjourney中的强变化、弱变化、局部重绘的本质区别以及其有多逆天的功能

开篇 Midjourney中有3个图片“微调”&#xff0c;它们分别为&#xff1a; 强变化&#xff1b;弱变化&#xff1b;局部重绘&#xff1b; 在Discord里分别都是用命令唤出的&#xff0c;但如今随着AI技术的发达在类似AI可人一类的纯图形化界面中&#xff0c;我们发觉这样的逆天…

NLP深度学习 DAY4:Word2Vec详解:两种模式(CBOW与Skip-gram)

用稀疏向量表示文本&#xff0c;即所谓的词袋模型在 NLP 有着悠久的历史。正如上文中介绍的&#xff0c;早在 2001年就开始使用密集向量表示词或词嵌入。Mikolov等人在2013年提出的创新技术是通过去除隐藏层&#xff0c;逼近目标&#xff0c;进而使这些单词嵌入的训练更加高效。…

【Linux】 冯诺依曼体系与计算机系统架构全解

Linux相关知识点可以通过点击以下链接进行学习一起加油&#xff01;初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G编译器make与Makefile自动化构建GDB调试器与Git版本控制工具Linux下进度条 冯诺依曼体系是现代计算机设计的基石&#xff0c;其统一存储和顺序执行理念推动…

【最后203篇系列】005 -QTV200 Online

说明 借着春节休假&#xff0c;把这部分完工&#xff0c;然后2025年将正式的把量化研究的成果进行产品化输出。 首先&#xff0c;我会将策略的执行从脚本挪到服务。做法是将策略的逻辑放在微服务里&#xff0c;作为一个接口&#xff0c;而由sniffer来触发策略执行。我想这样策…

Midjourney基础-常用修饰词+权重的用法大全

用好修饰词很关键 Midjourney要用除了掌握好提示词的写法&#xff0c;按照上一篇《做Midjourney最好图文教程-提示词公式以及高级参数讲解》画面主体 场景氛围 主体行为 构图方式 艺术风格 图像质量。 要画出有质感的内容我们必须要掌握好“修饰词”&#xff0c;这些修饰…

Deepseek的RL算法GRPO解读

在本文中&#xff0c;我们将深入探讨Deepseek采用的策略优化方法GRPO&#xff0c;并顺带介绍一些强化学习&#xff08;Reinforcement Learning, RL&#xff09;的基础知识&#xff0c;包括PPO等关键概念。 策略函数&#xff08;policy&#xff09; 在强化学习中&#xff0c; a…

神经网络和深度学习

应用 类型 为什么近几年飞速发展 数据增长&#xff0c;算力增长&#xff0c;算法革新 逻辑回归 向量化 浅层神经网络(Shallow neural network) 单条训练数据前向传播计算表达式 batch训练数据前向传播计算表达式 反向传播计算表达式 参数随机初始化 不能全部设为0 原因是同一…

python学opencv|读取图像(四十八)使用cv2.bitwise_xor()函数实现图像按位异或运算

【0】基础定义 按位与运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;全1取1&#xff0c;其余取0。 按位或运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;有1取1&#xff0c;其余取0。 按位取反运算&#xff1a;一个二进制数&#xff0c;0变1,1变0。 按…

关联传播和 Python 和 Scikit-learn 实现

文章目录 一、说明二、什么是 Affinity Propagation。2.1 先说Affinity 传播的工作原理2.2 更多细节2.3 传播两种类型的消息2.4 计算责任和可用性的分数2.4.1 责任2.4.2 可用性分解2.4.3 更新分数&#xff1a;集群是如何形成的2.4.4 估计集群本身的数量。 三、亲和力传播的一些…

【etcd】二进制安装etcd

由于生产服务器不能使用yum 安装 etcd ,或者 安装的etcd 版本比较老&#xff0c;这里介绍一个使用二进制安装的方式。 根据安装文档编写一个下载脚本即可 &#xff1a; 指定 etcd 的版本 提供了两个下载地址 一个 Google 一个 Github&#xff0c; 不过都需要外网 注释掉删除保…

企业知识管理平台助力企业创新与竞争力提升的有效策略探讨

内容概要 在当今快速发展的商业环境中&#xff0c;企业知识管理平台的构建显得至关重要。它不仅为企业的知识资源提供了一个整合与分享的空间&#xff0c;还为企业的创新与竞争力提升提供了策略支持。本文将深入探讨企业知识管理平台的关键要素&#xff0c;包括知识获取、存储…

Java多线程——线程安全性

线程安全性 当多个线程访问某个类时&#xff0c;这个类始终都能表现出正确的行为&#xff0c;那么就称这个类是线程安全的 public class A {public void test(){//....} }无状态对象是线程安全的&#xff0c;其不包含任何域&#xff0c;也不包含任何对其他类中域的引用&#…

Windows安装Miniconda和PySide6以及配置PyCharm

目录 1. 选择Miniconda 2. 下载Miniconda 3. 安装Miniconda 4. 在base环境下创建pyside6环境 5. 安装pyside6环境 6. 配置PyCharm环境 7. 运行第一个程序效果 1. 选择Miniconda 选择Miniconda而没有选择Anaconda&#xff0c;是因为它是一个更小的Anaconda发行版&#x…

C++传送锚点的内存寻址:内存管理

文章目录 1.C/C内存分布回顾2.C内存管理2.1 内存申请2.2 operator new与operator delete函数2.3 定位new表达式 3.关于内存管理的常见知识点3.1 malloc/free和new/delete的区别3.2 内存泄漏 希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力&#xff01; 继C语…

循序渐进kubernetes-RBAC(Role-Based Access Control)

文章目录 概要Kubernetes API了解 Kubernetes 中的 RBACRoles and Role Bindings:ClusterRoles and ClusterRoleBindings检查访问权限&#xff1a;外部用户结论 概要 Kubernetes 是容器化应用的强大引擎&#xff0c;但仅仅关注部署和扩展远远不够&#xff0c;集群的安全同样至…

《从因果关系的角度学习失真不变表示以用于图像恢复》学习笔记

paper&#xff1a;2303.06859 GitHub&#xff1a;lixinustc/Causal-IR-DIL: Distortion invariant feature learning for image restoration from a causality perspective 2023 CVPR 目录 摘要 1、介绍 1.1 图像修复任务 1.2 失真不变表示学习 1.3 因果效应估计的挑战…

亚博microros小车-原生ubuntu支持系列:16 机器人状态估计

本来想测试下gmapping建图&#xff0c;但是底层依赖了yahboomcar_bringup做底层的数据处理&#xff0c;所以先把依赖的工程导入。 程序启动后&#xff0c;会订阅imu和odom数据&#xff0c;过滤掉一部分的imu数据后&#xff0c;然后与odom数据进行融合&#xff0c;最后输出一个…

Greenplum临时表未清除导致库龄过高处理

1.问题 Greenplum集群segment后台日志报错 2.回收库龄 master上执行 vacuumdb -F -d cxy vacuumdb -F -d template1 vacuumdb -F -d rptdb 3.回收完成后检查 仍然发现segment还是有库龄报警警告信息发出 4.检查 4.1 在master上检查库年龄 SELECT datname, datfrozen…