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

装箱 Box 数据类型

装箱是最简单直接的一种智能指针,它的类型是Box<T>。装箱使我们可以把数据存储到堆上,并在栈上保留一个指向堆数据的指针。装箱操作常常被用于下面的场景:

  1. 当你拥有一个无法在编译时确定大小的类型,但又想使用这个类型的值时。
  2. 当你需要传递大量数据的所有权,但又不希望产生大量数据的复制行为时。
  3. 当你希望拥有一个实现了指定trait的类型值,但又不关心具体的类型时。
fn main() {let b = Box::new(5);println!("b = {}", *b + 3);println!("b = {}", b);
}

常规引用就是一种类型的指针,你可以将指针形象地理解为一个箭头,它会指向存储在别处的某个值。

装箱类似于常规指针,也可以通过解引用来获取装箱实际的值,代码中*b就是如此。这个定义和Go中的unsafe.Pointer非常类似,所有具体类型的指针都可以用一种类型的指针来表示。

定义递归类型

RUST必须在编译时知道每一种类型占据的空间大小,但有一种递归的类型却无法在编译时被确定具体大小。比如下面例子中的链表:

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

我们尝试使用枚举来表达一个持有i32值的链表数据类型,通过不断嵌套元组的形式最终组成一个列表。

但程序无法编译通过,RUST认为这个类型拥有无限大小,无法确认类型所占用的存储空间大小。

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

RUST如何计算Message类型的大小?为了计算Message值需要多大的存储空间,RUST会遍历枚举中的每一个成员来找到需要最大空间的那个变体。Message::Quit不需要占用任何空间,Message::Move需要两个存储i32值的空间,以此类推。

因为指针大小是恒定的,要改变这样无穷递归的情况,就应该将Cons变体中存放一个Box<T>而不是直接存放另外一个List值,而Box<T>则会指向下一个List并存储在堆上。

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

新的Cons变体中需要一部分存储i32的空间和一部分存储装箱指针的空间,这样调整之后,List值都只需要占用一个i32值加上一个装箱指针的空间。通过使用装箱,我们打破了无限递归的过程,进而使编译器可以计算出List值所占用的空间。

Box<T>属于智能指针的一种,因为它实现了Defer trait,所以允许我们将Box<T>的值当做引用来对待。当一个Box<T>值离开作用域时,因为它实现了Drop trait,所以Box<T>指向的堆数据会自动地被清理释放。==

实现 Defer trait将类型视作引用

定义我们自己的智能指针,Box<T>类型最终被定义为拥有两个元素的元组结构体。结构体给Deref trait实现了defef方法,该方法会借用self并返回一个指向元素第一个元素的引用。

RUST所有权系统决定了deref方法需要返回一个引用。假设deref方法使编译器直接返回了值而不是指向值的引用,那么这个值就会被移除self。在大多数使用解引用运算符的场景下,我们并不希望获取MyBox<T>内部值的所有权。

type Target = T 定义了Deref trait的一个关联类型。我们在deref方法体中返回指向第一个元素的引用,进而允许调用者通过*运算符访问值。

use std::ops::Deref;struct MyBox<T>(T, T);impl<T> MyBox<T> {fn new(x: T, y: T) -> MyBox<T> {MyBox(x, y)}
}impl<T> Deref for MyBox<T> {type Target = T;fn deref(&self) -> &Self::Target {&self.0}
}fn hello(language: &str) {println!("Hello, {}", language)
}fn main() {let m = MyBox::new(String::from("Rust"), String::from("Go"));hello(&m);
}

在没有Deref trait的情况下,编译器只能对&形式的常规引用执行解引用操作。deref方法使编译器可以从任何实现了Deref的类型中获取值,并能够调用deref方法来获取一个可以进行解引用的引用。

解引用转换(deref coercion)是RUST为函数和方法的参数提供的一种便捷特性。当某个类型T实现了Deref trait时,它能够将T的引用转换为T经过Deref操作之后的引用。

函数hello接收&str字符串切片类型,自定义MyBox<T>自动解引用为元组第一个元素的引用&String。函数体需要&str类型,但现在传入的是&String类型,为什么编译依然能够通过呢?

能够使用&String的原因也是因为解引用操作,编译器可以自动将&String类型的参数强制转换为&str类型。

解引用转换与可变性

使用Deref trait能够重载不可变引用的*运算符,与之类似,使用DerefMut trait能够重载可变引用*运算符。

RUST会在类型与trait满足下面三种情形下执行解引用转换:

  • T: Deref<Traget=U>时,允许&T转换为&U
  • T: DerefMut<Target=U>时,允许&mut T转换为&mut U
  • T: Deref<Target=U>时,允许&mut T转换为&U

第三种情况,RUST会将一个可变引用转换为一个不可变引用。但这个过程绝对不可逆,也就是说不可变引用永远不可能转换为可变引用。因为按照借用规则,如果存在一个可变引用,那么它就必须是唯一的引用,否则程序无法编译通过。

Drop trait在清理时运行代码

我们可以通过实现Drop trait来指定值离开作用域时需要运行的代码。Drop trait要求实现一个接收self可变引用作为参数的drop函数。

Go语言中的defer也有类似的能力,在离开作用域时执行代码。

struct CustomSmartPointer {data: String,
}impl Drop for CustomSmartPointer {fn drop(&mut self) {println!("Dropping CustomSmartPoint with data {}", self.data)}
}fn main() {let c = CustomSmartPointer {data: String::from("my stuff"),};let d = CustomSmartPointer {data: String::from("other stuff"),};// drop(c);println!("main")
}

这段代码没有显示地将Drop trait引入作用域,因为它已经被包含在预导入模块中。RUST会在实例离开作用域时自动调用我们编写的drop代码。因为变量的丢弃顺序与创建顺序相反,所以,dc之前被丢弃。

RUST并不允许我们手动调用Drop traitdrop方法,因为自动和手动会两次触发drop,这种行为试图对同一个值清理两次而导致重复释放(double free)错误。

如果需要提前清理一个值,可以调用标准库中的std::mem:drop函数来提前清理某个值。代码注释部分的代码就用来提前清理c

使用Drop无需担心正在使用的值被意外清理掉:所有权系统会保证所有引用的有效性,而drop只会在确定不使用这个值时被调用一次。

相关文章:

装箱 Box 数据类型

装箱是最简单直接的一种智能指针&#xff0c;它的类型是Box<T>。装箱使我们可以把数据存储到堆上&#xff0c;并在栈上保留一个指向堆数据的指针。装箱操作常常被用于下面的场景&#xff1a; 当你拥有一个无法在编译时确定大小的类型&#xff0c;但又想使用这个类型的值…...

多传感器融合SLAM在自动驾驶方向的初步探索的记录

1. VIO的不可观问题 现有的VIO都是解决的六自由度的问题, 但是对于行驶在路面上的车来说, 通常情况下不会有roll与z方向的自由度, 而且车体模型限制了不可能有纯yaw的变换. 同时由于IMU在Z轴上与roll, pitch上激励不足, 会导致IMU在初始化过程中尺度不准以及重力方向估计错误,…...

ffmpeg与opencv-python处理视频

安装 opencv pip install opencv-pythonFFmpeg 1.下载 FFmpeg 访问FFmpeg官方网站。选择 “Windows builds from gyan.dev” 链接&#xff0c;这会带您到一个包含最新版本 FFmpeg Windows 构建的页面。选择一个适合您系统的版本&#xff08;例如&#xff0c;32位或64位&…...

java 操作git

​ 实现功能&#xff1a;借助jgit实现拉取文件&#xff0c;并返回文件路径清单 <!-- 依赖库 版本号有自行选择&#xff0c;只是需要注意支持的jdk版本即可&#xff0c;我使用的是jdk1.8--> <dependency><groupId>org.eclipse.jgit</groupId><artif…...

Linux 导入、导出 MySQL 数据库命令

一、导出数据库 1、导出完整数据&#xff1a;表结构数据 mysqldump -u用户名 -p 数据库名 > 数据库名.sql 举例&#xff1a;以下命令可以导出 abc 数据库的数据和表结构 /usr/local/mysql/bin/mysqldump -uroot -p abc > abc.sql2、只导出表结构 mysqldump -u用户名 -p…...

华为数通---BFD多跳检测示例

定义 双向转发检测BFD&#xff08;Bidirectional Forwarding Detection&#xff09;是一种全网统一的检测机制&#xff0c;用于快速检测、监控网络中链路或者IP路由的转发连通状况。 目的 为了减小设备故障对业务的影响&#xff0c;提高网络的可靠性&#xff0c;网络设备需要…...

AWS 日志分析工具

当您的网络资源托管在 AWS 中时&#xff0c;需要定期监控您的 AWS CloudTrail 日志、Amazon S3 服务器日志和 AWS ELB 日志等云日志&#xff0c;以降低任何潜在的安全风险、识别严重错误并确保满足所有合规性法规。 什么是 Amazon S3 Amazon Simple Storage Service&#xff…...

gitLab 和Idea分支合并

以下二选1即可完成分支合并建议第一种简单有效 Idea合并方式 切换到被合并的分支&#xff0c;如我想把0701的内容合并到dev&#xff0c;切换到dev分支&#xff0c;然后再点击merge然后选择要合并的分支&#xff0c;即可,此时git上的代码没有更新只是把代码合到本地需要pull才…...

关于 mapboxgl 的常用方法及效果

给地图标记点 实现效果 /*** 在地图上添加标记点* point: [lng, lat]* color: #83f7a0*/addMarkerOnMap(point, color #83f7a0) {const marker new mapboxgl.Marker({draggable: false,color: color,}).setLngLat(point).addTo(this.map);this.markersList.push(marker);},…...

C语言——二级指针

指针变量也是变量&#xff0c;是变量就有地址&#xff0c;那么指针变量的地址存放在哪里&#xff1f;——这就是二期指针 int a 10;int *pa &a;int **ppa &pa;//a的地址存放在pa中&#xff0c;pa的地址存放在ppa中。 //pa是一级指针&#xff0c;ppa是二级指针。 对…...

股市复苏中的明懿金汇:抓住新机遇

2023年对于明懿金汇来说是充满挑战与机遇的一年。面对复杂多变的市场环境&#xff0c;明懿金汇展现了其对市场趋势的敏锐洞察和卓越的策略适应能力。以下是该公司在2023年的主要投资策略和市场适应方式的详细分析。 随着2023年中国股市迎来反弹&#xff0c;明懿金汇迅速调整了…...

Spacemesh、Kaspa和Chia的全面对比!

当今区块链领域&#xff0c;PoST&#xff08;Proof of Space and Time&#xff09;共识算法引领着一股新的技术浪潮。在这个热潮下&#xff0c;Chia项目作为PoST共识机制的经典项目&#xff0c;和目前算力赛道备受瞩目的Kaspa项目&#xff0c;都是不可忽视的存在。虽然这两个项…...

【HTML语法】

HTML语法 1. HTML语法1.1 HTML编辑器1.2 HTML模板1.3 标签示例1.4 常见的HTML标签1.51.61.71.81.91.101.11 学习网站&#xff1a;https://www.runoob.com/html/html-tutorial.html 1. HTML语法 HTML&#xff08;全称 Hypertext Markup Language&#xff0c;超文本标记语言&…...

ROS报错:RLException:Invalid roslaunch XML Syntax: mismatched tag:

运行roslaunch文件提示&#xff1a; RLException:Invalid roslaunch XML Syntax: mismatched tag: line 45&#xff0c; column 2 The traceback for the exception was written to the log file. j 解决办法&#xff1a; line45 行多了标签&#xff1a;</node> 另外…...

C语言实现快速排序

完整代码&#xff1a; #include<stdio.h>//用第一个元素将待排序序列划分成左右两个部分&#xff0c;返回排序后low的位置&#xff0c;即枢轴的位置 int partition(int arr[],int low,int high){//让待排序序列中的第一个元素成为基准int pivotarr[low];//lowhigh代表一…...

ChatGPT对于当今的社会或科技发展有何重要性?

ChatGPT对于当今社会和科技发展的重要性在于&#xff1a; 促进社交交流&#xff1a;ChatGPT可以为人们提供全天候的在线聊天服务&#xff0c;连接人与人之间的沟通交流&#xff0c;改善社交沟通方式。 提高有效性和效率&#xff1a;人们可以通过ChatGPT获得快速和精确的信息&a…...

宝塔是可以切换mongodb版本的

在软件商店&#xff0c;搜索monggodb&#xff0c;点击设置。点击第三个标签版本切换即可。但是前提要删除所有非系统数据库。 删除数据库方法&#xff1a; 要在 MongoDB 中删除一个数据库&#xff0c;可以使用 dropDatabase() 命令。请注意&#xff0c;在执行此操作之前&#x…...

16、XSS——会话管理

文章目录 一、web会话管理概述1.1 会话管理1.2 为什么需要会话管理&#xff1f;1.3 常见的web应用会话管理的方式 二、会话管理方式2.1 基于server端的session的管理方式2.2 cookie-based的管理方式2.3 token-based的管理方式 三、安全问题 一、web会话管理概述 1.1 会话管理 …...

稀疏矩阵的操作(数据结构实训)

题目&#xff1a; 标准输入输出 题目描述&#xff1a; 稀疏矩阵可以采用三元组存储。 输入&#xff1a; 输入包含若干个测试用例&#xff0c;每个测试用例的第一行为两个正整数m,n(1<m,n<100),表示矩阵的行数和列数,接下来m行&#xff0c;每行n个整数&#xff0c;表示稀疏…...

sqlite - sqlite3_exec - c++回调函数的处理

文章目录 sqlite - sqlite3_exec - c回调函数的处理概述笔记回调赋值实现用到的数据结构回调分发函数的实现具体的回调处理sqlite3_exe执行完后, 行集的具体处理END sqlite - sqlite3_exec - c回调函数的处理 概述 以前给客户写了个小程序, 处理sqlite执行sql时, 给定回调, 等…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

SQL注入篇-sqlmap的配置和使用

在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap&#xff0c;但是由于很多朋友看不了解命令行格式&#xff0c;所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习&#xff0c;链接&#xff1a;https://wwhc.lanzoue.com/ifJY32ybh6vc…...

6.计算机网络核心知识点精要手册

计算机网络核心知识点精要手册 1.协议基础篇 网络协议三要素 语法&#xff1a;数据与控制信息的结构或格式&#xff0c;如同语言中的语法规则语义&#xff1a;控制信息的具体含义和响应方式&#xff0c;规定通信双方"说什么"同步&#xff1a;事件执行的顺序与时序…...