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

Rust 学习笔记:Box<T>

Rust 学习笔记:Box

  • Rust 学习笔记:Box<T\>
    • Box\<T> 简介
    • 使用 Box\<T\> 在堆上存储数据
    • 启用带有 box 的递归类型
      • 关于 cons 列表的介绍
      • 计算非递归类型的大小
      • 使用 Box\<T\> 获取大小已知的递归类型

Rust 学习笔记:Box<T>

指针是在内存中包含地址的变量的一般概念。这个地址引用或“指向”其他一些数据。在 Rust 中最常见的指针类型是引用,由 & 符号表示,并借用它们所指向的值。除了引用数据之外,它们没有任何特殊功能,也没有开销。

智能指针是一种像指针一样的数据结构,但还具有额外的元数据和功能。Rust 在标准库中定义了各种智能指针,这些指针提供的功能超出了引用所提供的功能。

具有所有权和借用概念的 Rust 在引用和智能指针之间有一个额外的区别:引用只借用数据,而在许多情况下,智能指针拥有它们所指向的数据。

我们遇到了一些智能指针:String 和 Vec<T>。这两种类型都算作智能指针,因为它们拥有一些内存,并允许对其进行操作。它们还具有元数据和额外的功能或保证。例如,String 将其容量存储为元数据,并具有确保其数据始终是有效的 UTF-8 的额外能力。

智能指针通常使用结构体实现。与普通结构体不同,智能指针实现了 Deref 和Drop trait。Deref trait 允许智能指针结构体的实例表现得像引用一样,这样就可以编写代码来使用引用或智能指针。Drop trait 允许自定义当智能指针的实例超出作用域时运行的代码。

Box<T> 简介

最直接的智能指针是 Box<T>,它运行将数据存储在堆中而不是栈中,留在栈上的是指向堆数据的指针。

Box<T> 没有性能开销,但它们也没有太多额外的功能。最常在以下情况下使用它:

  • 当你的类型在编译时无法知道其大小,并且你希望在需要精确大小的上下文中使用该类型的值时

  • 当你有大量的数据,你想要转移所有权,但要确保数据不会被复制时

  • 当你想拥有一个值,你只关心它是一个实现了特定特性的类型,而不是一个特定的类型

我们将在下文中演示第一种情况。在第二种情况下,传输大量数据的所有权可能需要很长时间,因为数据是在栈上复制的。为了在这种情况下提高性能,我们可以将大量数据存储在堆中的盒子中。然后,只有少量的指针数据在栈上被复制,而它引用的数据留在堆上的一个地方。第三种情况被称为 trait 对象,后续文章将专门讨论了这个主题。

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

首先介绍 Box<T> 的语法以及如何与存储在 Box<T> 中的值进行交互。

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

我们将变量 b 定义为具有指向值 5 的 box 的值,该值在堆上分配。这个程序将输出 b = 5。在这种情况下,我们可以访问 box 中的数据,就像我们访问栈中的数据一样。

当一个 box 超出作用域时,就像 main 语句末尾的 b 变量那样,它将被释放。对 box(存储在栈上)和它所指向的数据(存储在堆上)都进行释放。

将单个值放在堆上并不是很有用,在栈上使用单个 i32 这样的值更合适。

Box<T> 在定义类型时更有用。

启用带有 box 的递归类型

递归类型的值可以有另一个相同类型的值作为其本身的一部分。递归类型造成了一个问题,因为 Rust 需要在编译时知道一个类型占用了多少空间。然而,递归类型的值的嵌套理论上可以无限地继续下去,因此 Rust 无法知道值需要多少空间。因为 box 的大小是已知的,所以我们可以通过在递归类型定义中插入一个 box 来启用递归类型。

作为递归类型的一个示例,让我们研究一下 cons 列表。这是函数式编程语言中常见的一种数据类型。

关于 cons 列表的介绍

cons 列表是一种来自 Lisp 编程语言的数据结构,由嵌套对组成,是 Lisp 版本的链表。它的名字来自于 Lisp 中的c ons 函数(construct function 的缩写),它从它的两个参数构造一个新的 pair。通过对由一个值和另一个值组成的对调用 cons,我们可以构造由递归对组成的 cons 列表。

例如,下面是一个 cons 列表的伪代码表示,其中包含列表 1、2、3,每一对都在括号中:

(1, (2, (3, Nil)))

cons 列表中的每一项包含两个元素:当前项的值和下一项的值。列表中的最后一项只包含一个名为 Nil 的值,没有下一项。

cons 列表不是Rust中常用的数据结构。但从本章的 cons 列表开始,我们可以探索 box 如何让我们定义递归数据类型。

下列代码包含了 cons 列表的枚举定义。

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

注意,这段代码还不能编译,因为 List 类型没有已知的大小,我们将对此进行演示。

尝试构建一个 cons 列表:

use crate::List::{Cons, Nil};fn main() {let list = Cons(1, Cons(2, Cons(3, Nil)));
}

第一个 Cons 值保存 1 和另一个 List 值。这个 List 值是另一个 Cons 值,它包含 2 和另一 List 值。这个 List 值是另一个 Cons 值,它包含 3 和一个 List 值,最后是 Nil,这是表示列表结束的非递归变体。

尝试运行这段代码,报错:

在这里插入图片描述

错误显示 List 类型“具有无限大小”。原因是我们用递归的变量定义了 List:它直接保存自身的另一个值。因此,Rust 无法计算出它需要多少空间来存储 List 值。

让我们分析一下为什么会出现这个错误。首先,我们来看一下 Rust 如何决定存储非递归类型的值需要多少空间。

计算非递归类型的大小

以一个 Message 枚举为例:

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

为了确定为 Message 值分配多少空间,Rust 遍历每个变体,以查看哪个变体需要最多的空间。Message::Quit 不需要任何空间,Message::Move 占用两个 i32 值大小的空间,以此类推。因为只使用一个变体,所以 Message 值所需的最大空间就是存储其最大变体所需的空间。

与此形成对比的是,当 Rust 试图确定 List 枚举这样的递归类型需要多少空间时发生的情况。编译器首先查看 Cons 变量,它包含一个 i32 类型的值和一个 List 类型的值。因此,Cons 需要的空间量等于 i32 的大小加上 List 的大小。为了计算出 List 类型需要多少内存,编译器从 Cons 变量开始,这个过程无限地继续下去。

在这里插入图片描述

使用 Box<T> 获取大小已知的递归类型

因为 Rust 不能计算出为递归定义的类型分配多少空间,编译器给出了一个错误,并给出了这个有用的建议:

在这里插入图片描述

在这个建议中,间接意味着不是直接存储一个值,而是通过存储指向该值的指针来改变数据结构,从而间接存储该值。

因为 Box<T> 是一个指针,指针的大小不会根据它所指向的数据量而改变。这意味着我们可以在 Cons 变量中放入 Box<T>,而不是直接放入另一个 List 值。Box<T> 将指向下一个 List 值,该值将位于堆上,而不是在 Cons 变量中。

从概念上讲,我们仍然有一个列表,创建了包含其他列表的列表。但是这个实现现在更像是将项放在另一个项旁边,而不是放在另一个项内部。

修改代码:

enum List {Cons(i32, Box<List>),Nil,
}use crate::List::{Cons, Nil};fn main() {let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

一个 Cons 变量 = 一个 i32 + 一个Box<T> 指针。Nil 不存储任何值,因此它比 Cons 变量需要更少的空间。通过使用盒子,我们打破了无限的递归链,因此编译器可以计算出存储 List 值所需的大小。

在这里插入图片描述

盒子只提供间接分配和堆分配,没有任何其他特殊功能,也没有这些特殊功能所带来的性能开销,因此它们在像 cons 列表这样的情况下非常有用,其中间接是我们唯一需要的特性。

Box<T> 类型是一个智能指针,因为它实现了 Deref trait,它允许 Box<T> 值被当作引用来对待。当 Box<T> 值超出作用域时,由于 Drop trait 的实现,该指针所指向的堆数据也会被清理。

相关文章:

Rust 学习笔记:Box<T>

Rust 学习笔记&#xff1a;Box Rust 学习笔记&#xff1a;Box<T\>Box\<T> 简介使用 Box\<T\> 在堆上存储数据启用带有 box 的递归类型关于 cons 列表的介绍计算非递归类型的大小使用 Box\<T\> 获取大小已知的递归类型 Rust 学习笔记&#xff1a;Box<…...

C# 从 ConcurrentDictionary 中取出并移除第一个元素

C# 从 ConcurrentDictionary 中取出并移除第一个元素 要从 ConcurrentDictionary<byte, int> 中取出并移除第一个元素&#xff0c;需要结合 遍历 和 原子移除操作。由于 ConcurrentDictionary 是无序集合&#xff0c;"第一个元素" 通常是指最早添加的元素&…...

操作系统学习(十三)——Linux

一、Linux Linux 是一种类 Unix 的自由开源操作系统内核&#xff0c;由芬兰人 Linus Torvalds 于 1991 年首次发布。如今它广泛应用于服务器、桌面、嵌入式设备、移动设备&#xff08;如 Android&#xff09;等领域。 设计思想&#xff1a; 原则描述模块化与可移植性Linux 内…...

NLP学习路线图(二十二): 循环神经网络(RNN)

在自然语言处理&#xff08;NLP&#xff09;的广阔天地中&#xff0c;序列数据是绝对的核心——无论是流淌的文本、连续的语音还是跳跃的时间序列&#xff0c;都蕴含着前后紧密关联的信息。传统神经网络如同面对一幅打散的拼图&#xff0c;无法理解词语间的顺序关系&#xff0c…...

每日一C(1)C语言的内存分布

目录 代码区 常量区 全局/静态区 初始化数据段&#xff08;.data&#xff09; 未初始化数据段&#xff08;.bss&#xff09; 堆区 栈区 总结 今天我们学习的是C语言的内存分布&#xff0c;以及这些分区所存储的内容和其特点。今天的思维导图如下。 C语言作为一款直接处…...

Photoshop使用钢笔绘制图形

1、绘制脸部路径 选择钢笔工具&#xff0c;再选择“路径”。 基于两个点绘制一个弯曲的曲线 使用Alt键移动单个点&#xff0c;该点决定了后续的曲线方向 继续绘制第3个点 最后一个点首尾是同一个点&#xff0c;使用钢笔保证是闭合回路。 以同样的方式绘制2个眼睛外框。 使用椭…...

应用层协议:HTTP

目录 HTTP&#xff1a;超文本传输协议 1.1 HTTP报文 1.1.1 请求报文 1.1.2 响应报文 1.2 HTTP请求过程和原理 1.2.1 请求过程 1、域名&#xff08;DNS&#xff09;解析 2、建立TCP连接&#xff08;三次握手&#xff09; 3、发送HTTP请求 4、服务器处理请求 5、返回H…...

复习——C++

1、scanf和scanf_s区别 2、取地址&#xff0c;输出 char ba; char* p&b; cout<<*p; cout<<p; p(char*)"abc"; cout<<*p; cout<<p; cout<<(void*)p; 取地址&#xff0c;把b的地址给p 输出*p&#xff0c;是输出p的空间内的值…...

SPI通信协议(软件SPI读取W25Q64)

SPI通信协议 文章目录 SPI通信协议1.SPI通信2.SPI硬件和软件规定2.1SPI硬件电路2.2移位示意图2.3SPI基本时序单元2.3.1起始和终止条件2.3.2交换一个字节&#xff08;模式1&#xff09; 2.4SPI波形分析&#xff08;辅助理解&#xff09;2.4.1发送指令2.4.2指定地址写2.4.3指定地…...

PostgreSQL-基于PgSQL17和11版本导出所有的超表建表语句

最新版本更新 https://code.jiangjiesheng.cn/article/368?fromcsdn 推荐 《高并发 & 微服务 & 性能调优实战案例100讲 源码下载》 1. 基于pgsql 17.4 研究 查询psql版本&#xff1a;SELECT version(); 查看已知1条建表语句和db中数据关系 SELECT create_hypert…...

JavaWeb:前后端分离开发-部门管理

今日内容 前后端分离开发 准备工作 页面布局 整体布局-头部布局 Container 布局容器 左侧布局 资料\04. 基础文件\layout/index.vue <script setup lang"ts"></script><template><div class"common-layout"><el-containe…...

ArcGIS计算多个栅格数据的平均栅格

3种方法计算多个栅格数据的平均栅格 1->使用“ 栅格计算器”工具 原理就是把多幅影像数据相加&#xff0c;然后除以个数&#xff0c;就能得到平均栅格。 2-> 使用“像元统计数据”工具&#xff0c;如果是ArcGIS pro&#xff0c;则是“像元统计”工具。使用这个工具可以…...

字节开源FlowGram:AI时代可视化工作流新利器

字节终于开源“扣子”同款引擎了&#xff01;FlowGram&#xff1a;AI 时代的可视化工作流利器 字节FlowGram创新性地融合图神经网络与多模态交互技术&#xff0c;构建了支持动态拓扑重构的可视化流程引擎。该系统通过引入 f ( G ) ( V ′ &#xff0c; E ′ ) f(\mathcal{G})…...

如何选择合适的分库分表策略

选择合适的分库分表策略需要综合考虑业务特点、数据规模、访问模式、技术成本等多方面因素。以下是系统性的选择思路和关键决策点&#xff1a; 一、核心决策因素 业务需求分析 数据规模&#xff1a;当前数据量&#xff08;如亿级&#xff09;、增长速度&#xff08;如每日新增百…...

(LeetCode 每日一题)3403. 从盒子中找出字典序最大的字符串 I (贪心+枚举)

题目&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 题目&#xff1a;贪心枚举字符串&#xff0c;时间复杂度0(n)。 最优解的长度一定是在[1,n-numFriends]之间。 字符串在前缀都相同的情况下&#xff0c;长度越长越大。 C版本&#xff1a; class Solution { public:st…...

GPIO的内部结构与功能解析

一、GPIO总体结构 总体构成 1.APB2(外设总线) APB2总线是微控制器内部连接CPU与外设&#xff08;如GPIO&#xff09;的总线&#xff0c;负责CPU对GPIO寄存器的读写访问&#xff0c;支持低速外设通信 2.寄存器 控制GPIO的配置&#xff08;输入/输出模式、上拉/下拉等&#x…...

Python训练打卡Day42

Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 在深度学习中&#xff0c;我们经常需要查看或修改模型中间层的输出或梯度。然而&#xff0c;标准的前向传播和反向传播过程通常是一个黑盒&#xff0c;我们很难直接访问中间层的信…...

深度学习中的负采样

深度学习中的负采样 负采样&#xff08;Negative Sampling&#xff09; 是一种在训练大型分类或概率模型&#xff08;尤其是在输出类别很多时&#xff09;中&#xff0c;用来加速训练、降低计算量的方法。 它常用于&#xff1a; 词向量训练&#xff08;如 Word2Vec&#xff…...

php7+mysql5.6单用户中医处方管理系统V1.0

php7mysql5.6中医处方管理系统说明文档 一、系统简介 ----------- 本系统是一款专为中医诊所设计的处方管理系统&#xff0c;基于PHPMySQL开发&#xff0c;不依赖第三方框架&#xff0c;采用原生HTML5CSS3AJAX技术&#xff0c;适配手机和电脑访问。 系统支持药品管理、处方开…...

Java 大视界 — Java 大数据在智能安防视频监控中的异常事件快速响应与处理机制

/*Java 大数据在智能安防视频监控中的异常事件快速响应与处理机制&#xff08;简化示例&#xff09;*/// 1. Event.java - 异常事件模型 package com.security.model;public class Event {private String id;private String type; // 如: "入侵", "火警"pr…...

智慧物流园区整体解决方案

该智慧物流园区整体解决方案借助云计算、物联网、ICT 等技术,从咨询规划阶段介入,整合供应链上下游资源,实现物流自动化、信息化与智能化。方案涵盖智慧仓储管理(如自动化立体仓储系统、温湿度监控)、智慧物流(运输管理系统 TMS、GPS 监控)、智慧车辆管理(定位、调度、…...

审批流程管理系统开发记录:layui前端交互的实践

一、需求拆解与技术选型 本次开发围绕企业审批流程管理场景,需实现以下核心功能: 前端申请表单与流程进度可视化底部滑动审批弹窗交互多版本MySQL数据库支持流程数据的增删改查与状态管理技术栈选择: 前端采用LayUI框架,利用其时间线组件(lay-timeline)实现流程进度展示…...

【会员专享数据】1960—2023年我国省市县三级逐年降水量数据(Shp/Excel格式)

之前我们分享过1960-2023年我国0.1分辨率的逐日、逐月、逐年降水栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff0c;是研究者Jinlong Hu与Chiyuan Miao分享在Zenodo平台上的数据&#xff0c;很多小伙伴拿到数据后反馈栅格数据不太方便使用&#xff0c;问我…...

2025年精通MVCC

今年找工作&#xff0c;无一例外又问到了MVCC这个知识点。几乎每次换工作都会被问到这个面试有用&#xff0c;工作毫无 * 用的知识。但是环境就是这样&#xff0c;既然如此&#xff0c;我们用一篇文章彻底搞懂MVCC 1.MVCC是什么 MVCC&#xff08;Multi-Version Concurrency C…...

硬路由与软路由

目录 核心区别 ⚙️ 性能与功能定位 如何选择&#xff1f; 核心区别 硬路由&#xff1a; 本质&#xff1a; 专用的硬件设备。构成&#xff1a; 厂家将特定的路由器操作系统&#xff08;通常是高度定制化、封闭或精简的&#xff09;固化在专用的硬件平台上。硬件&#xff1a…...

OpenCV C++ 心形雨动画

❤️ OpenCV C 心形雨动画 ❤️ 本文将引导你使用 C 和 OpenCV 库创建一个可爱的心形雨动画。在这个动画中&#xff0c;心形会从屏幕顶部的随机位置落下&#xff0c;模拟下雨的效果。使用opencv定制自己的专属背景 目录 简介先决条件核心概念实现步骤 创建项目定义心形结构…...

Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结

Fullstack 面试复习笔记&#xff1a;Java 基础语法 / 核心特性体系化总结 上一篇笔记&#xff1a;Fullstack 面试复习笔记&#xff1a;操作系统 / 网络 / HTTP / 设计模式梳理 目前上来说&#xff0c;这个系列的笔记本质上来说&#xff0c;是对不理解的知识点进行的一个梳理&…...

安卓Compose实现鱼骨加载中效果

安卓Compose实现鱼骨加载中效果 文章目录 安卓Compose实现鱼骨加载中效果背景与简介适用场景Compose骨架屏与传统View实现对比Shimmer动画原理简介常见问题与优化建议参考资料 本文首发地址 https://h89.cn/archives/404.html 背景与简介 在移动应用开发中&#xff0c;加载中占…...

使用qt 定义全局钩子 捕获系统的键盘事件

使用qt 定义全局钩子 捕获系统的键盘事件 即使焦点不在自定义软件上&#xff0c;也能够触发 以下待接口代码&#xff1a; class Hook :public QObject { Q_OBJECT public: Hook(); enum Type { CTRL_E, CTRL_W, SPACE, Enter, C };//自定义枚举&#xff0c;定义“修改”、“撤回…...

FreeType 字体信息检查工具 - 现代C++实现

文章目录 获取字体的版权信息工具简介主要特点1. 现代C实现2. 完整的功能3. 健壮的错误处理4. 国际化支持 使用说明技术亮点 获取字体的版权信息 #include <iostream> // 标准输入输出流库 #include <string> // 字符串处理库 #include <vector>…...