Rust常用特型之Sized特型

在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。

一个Sized 类型是指 它所有的值在内存中有相同的大小,反之没有相同大小就是UnSized 类型。

Rust中几乎所有的类型都是Sized的,例如每个u64在内存中都是8个字节,每个(f32,f32,f32)元组的大小是12字节。甚至枚举也是Sized的。枚举的内存大小一般为能容纳最大字节数量变量的一个固定值。虽然Vec<T>它拥有一个分配在堆上的大小可变的缓冲区,但是Vec值本身是指向这个缓冲区的指针,同时包含了容量和长度,所以Vec<T>也是Sized 类型。

所有Sized 类型实现了 std::marker::Sized特型,这个特型为一个空特型 ,不包含任何方法或者关联类型。Rust 自动为所有能应用它的类型自动实现了它,你不能手动去实现Sized特型。Sized唯一使用的场景为类型变量约束, 一个类似T:Sized的约束需要类型T的大小在编译时是已知的。这一类的特型被称为marker特型,因为Rust语言使用它们来标记某些类型具有特定的行为。

然而,Rust也有少部分类型为unsized类型,他们的值占用的空间并不是相同的。例如,字符串切片类型str(注意,没有前面的&),是unsized。字符串文字值hellobig是对字符串切片str的引用,而这两个切片分别占用5和3字节。但是&str是固定大小的,它是个两字节的胖指针。同理数组切片类型[T]也是unsized,一个共享的[u8]的引用&[u8]可以指向任意大小的切片 。因为str[T]类型用于声明可变大小的值,所以他们是unsized类型。

另一类常见的unsized类型为dyn类型,也就是特型对象。特型对象是一个指向实现了某个特型的值的指针。例如,类型&dyn std::io::WriteBox<dyn std::io::Write> 是指向某些实现了Write特型的引用。

这里要澄清一下,特型对象和对特型对象的引用之间的关系就和切片及切片的引用一样,有些混淆,特型对象是指dyn write, 而&dyn Write是对特型对象的引用。前者是unsized类型,因为实现Write的对象是多种多样的,大小不固定的。而后者是Sized类型,因为它其实是一个胖指针。

Rust 并不能使用变量存储unsize类型的值或者将它们作为函数参数传递。你只能通过引用来处理他们,例如&strBox<dyn Write>,这些引用本身是sized类型。一个指向unsized值的指针是一个胖指针,它们占两个word宽,前者指向一个切片地址并同时附带切片的长度,后者指向一个指向特型对象地址和实现函数的虚拟表。

这里两个words的意思是常规指针使用一个word,他们使用两个words,是常规指针的两倍。

如果是切片的引用,那么它指针的第一个word是指向切片数据地址,第二个word是指向切片长度。

如果是特型对象的引用,那么它指针的第一个word是指向实现特型的值的地址,第二个word是指向虚拟函数表(vtable)。

特型对象和切片是有些类似的,他们都缺乏使用自身的必要信息。例如你无法在不知道切片长度的时候去索引切片,或者说你在无法知道具体特型对象具体实现的时候去调用特型对象的方法。这两种情况下,都使用胖指针来补充底层类型所缺乏的信息,例如切片长度或者虚拟函数表。缺失的静态信息被动态信息替代。

由于unsized类型受到较大限制,因此平常使用泛型函数时应该严格约束T:Sized。实际上,这种约束太常见了,以致Rust把它作为了一个默认隐式实现。如果你写struct S<T>,Rust知道你的意思其实为struct S<T:Sized> 。如果你真实意思并不是这样,你必须手动写出如下定义struct S<T: ?Sized>。 这里?Sized语法代表可以/可能不是Sized类型。例如你写了struct S<T:?Sized>,你就可以使用S<str>或者S<dyn Write>。此时S<str>或者S<dyn Write>的Box就是一个胖指针,而S<i32>或者S<String>的Box就是普通指针。

这里最后一句的意思是如果你的类型中使用/拥有/包含了unsized类型,那个你的类型也是unsized的,使用它时只能通过引用,此时引用为一个胖指针。

这里如果一个类型中同时包含了两个unsized类型的值,那么它的Box是一个胖指针能搞定的么?

这里结构体只有最后一个字段才能是unsized类型,所以一个结构体无法拥有两个unsized值,所以一个胖指针仍然能搞定。

尽管有这些限制,unsized类型使得Rust的类型系统更丝滑。阅读标准库文档,你会经常发现类型变量的约束为?Sized,这通常意味着该类型的值只是被指向,因此允许相关代码可以和处理普通值一样处理切片和特型对象。

这句话的意思是如果类型变量约束为默认的Sized,你就只能处理普通值而无法处理切片和特型对象了。所以使用?Sized既可以处理普通值,也可以处理切片和特型对象,提高了灵活性。

除了切片类型和特型对象,这里还有一类unsized类型。如果一个结构体的最后一个字段(只有最后一个字段)是无固定大小类型,那么这个结构就是无固定大小类型。如下例:

struct RcBox<T: ?Sized> {ref_count: usize,value: T,
}

上面的代码用来实现类似引用计数的一个功能。它的具体实现是使用一个结构体存储引用计数和类型T的值。当然上面的代码是简化版本。上述结构体的value字段的类型是T,Re<T>是可计数的引用,Rc<T>解引用一个指针到该字段,ref_count字段记录引用的数量。

真实的RcBox是标准库的一个内部实现细节,因此无法被外部使用。但是我们假定一下使用我们前面的定义,你可以在sized类型上使用RcBox,例如RcBox<String>,这样该结构也是一个有固定大小的结构类型。你也可以在RcBox上应用unsized类型,例如RcBox<dyn std::fmt::Display>,这样,RcBox<dyn Display>就是一个unsized结构类型。

你无法直接建立一个RcBox<dyn Display>值,间接的方法是,你先创建一个普通的,sized RcBox,它的value字段的类型是一个实现了Display特型的类型,例如String。因此,这个临时值为RcBox<String>,然后Rust可以上你从它的普通引用&RcBox<String>转化成一个胖指针引用&RcBox<dyn Display>,示例如下:

let boxed_lunch: RcBox<String> = RcBox {ref_count: 1,value: "lunch".to_string()
};
use std::fmt::Display;
let boxed_displayable: &RcBox<dyn Display> = &boxed_lunch;

这种转换可以在把值传递给函数时隐式发生,所以你可以将&RcBox<String>传递给一个接收&RcBox<dyn Display>参数的函数。

fn display(boxed: &RcBox<dyn Display>) {
println!("For your enjoyment: {}", &boxed.value);
}
display(&boxed_lunch);

它会产生如下输出

For your enjoyment: lunch

总结:

  • Sized 类型代表在编译时知道其固定大小的类型,Rust中绝大多数类型为Sized类型。Unsized类型则相反,主要有切片类型,特型对象以及Unsized Struct类型。
  • 结构体只有最后一个字段为unsized时才整体为unsized struct,如果不是最后一个字段,不允许出现unsized类型。
  • 涉及到特型对象时,有时不能直接建立某个unsized type的值,此时可以先创建一个普通值,然后将普通引用转换为涉及特型对象的胖指针引用。
  • ?Sized用在泛型函数的类型变量约束中,它代表可以是Sized,也可以是Unsized。还有一种!Sized几乎不会见,代表UnSized。Rust自动为所有泛型中的类型变量约束实现了T:Sized,所以平常可以不写略过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/889.shtml

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

相关文章

18 统计网站每日的访问次数

1.将竞赛的数据上传HDFS,查看数据的格式 通过浏览器访问hdfs,查看该文档前面的部分数据 每条数据的字段值之间使用逗号隔开的 &#xff0c;最终时间是第五个自动&#xff0c;获取第五个字段值的中的年月日。 2.通过Idea创建项目mr-raceData ,基础的配置 修改pom.xml,添加依赖 …

Spring Boot集成fastdfs快速入门Demo

1.什么是fastdfs FastDFS 是一个开源的高性能分布式文件系统&#xff08;DFS&#xff09;。它的主要功能包括&#xff1a;文件存储&#xff0c;文件同步和文件访问&#xff0c;以及高容量和负载平衡。主要解决了海量数据存储问题&#xff0c;特别适合以中小文件&#xff08;建议…

从零开始搭建网站(第二天)

今天把之前的htmlcssjs项目迁移过来&#xff0c;直接使用tspiniavue3vite组合&#xff0c;搭建过程可以看从零开始搭建性能完备的网站-思路过程&#xff08;1&#xff09;_自己架设一个芯参数网站-CSDN博客。之后安装一下volar扩展。迁移过来使用Vue重构时发现之前使用的左右两…

【Python】如何利用MinHash和LSH进行大规模文本数据去重

十年之前 我不认识你 你不属于我 我们还是一样 陪在一个陌生人左右 走过渐渐熟悉的街头 十年之后 我们是朋友 还可以问候 只是那种温柔 再也找不到拥抱的理由 情人最后难免沦为朋友 &#x1f3b5; 刘若英《十年》 去重逻辑实现 数据准备&#xff1a;在内…

学习-官方文档编辑方法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

电感与磁珠

电感是什么&#xff1f; 电感会通过产生感应电动势的方式来阻碍电流的变化&#xff0c;电流变化率越大&#xff0c;产生的感应电动势越大阻碍电流效果越明显。 [一]品质因数Q: 电感的品质因数Q值定义&#xff1a;电感的Q值也叫作品质因数&#xff0c;其为无功功率除以有功功率…

API请求报错 Required request body is missing问题解决

背景 在进行调用的时候&#xff0c;加载方法&#xff0c;提示以下错误 错误信息如下&#xff1a; {"code": 10001,"msg": "Required request body is missing: XXX","data": null,"extra": null }Required request body…

ubuntu22.04下编译ffmpeg和ffplay

Ubuntu22.04 下编译安装 ffmpeg 和 ffplay 一、下载源码包 1.1 官方下载链接&#xff1a;Download FFmpeg 可以手动下载&#xff0c;也可以命令行下载&#xff1a; wget http://www.ffmpeg.org/releases/ffmpeg-7.0.tar.xz 1.2 下载完解压 tar -xvf ffmpeg-7.0.tar.xz…

《深入浅出多模态》: 多模态经典模型:BLIP

🎉AI学习星球推荐: GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料,配有全面而有深度的专栏内容,包括不限于 前沿论文解读、资料共享、行业最新动态以、实践教程、求职…

多个路由器连接的PC端进行ping通信需要做的事

实验环境&#xff1a; 三台PC三台路由器&#xff0c;并且配置好IP 拓扑图&#xff1a; 需求描述&#xff1a; 在PC0进行与PC2的ping通信&#xff1a; 需求步骤&#xff1a; 1.1首先配置ip&#xff08;略过&#xff09; 1.2我们首先查看在只配置了IP的情况下&#xff0c;P…

Flask中的JWT认证构建安全的用户身份验证系统

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Flask中的JWT认证&#xff1a;构建安全的用户身份验证系统 随着Web应用程序的发展&#xf…

详解IIC通信协议以及FPGA实现

一、IIC简介 IIC也称为I2C&#xff08;Inter-Integrated Circuit&#xff09;由飞利浦公司&#xff08;现在的恩智浦半导体&#xff09;开发&#xff0c;是一种用于短距离数字通信的串行&#xff0c;同步&#xff0c;半双工通信接口协议&#xff1b;传输在标准模式下可以达到10…

【C语言】指针详解(五)

目录 1.字符指针 1.1常量字符串 2.指针数组 3.数组指针 1.字符指针 字符指针就是指向字符的指针&#xff0c;字符指针可以存储字符变量的地址。 举例如下&#xff1a; 定义指针变量pa存a的地址&#xff0c;改变*pa的值&#xff0c;a也会随之改变 。 1.1常量字符串 &#x1f…

代码随想录算法训练营第三十八天|509. 斐波那契数,70.爬楼梯,746. 使用最小花费爬楼梯

动态规划(DP) 如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 所以动态规划中每一个状态一定是由上一个状态推导出来的 一、动态规划包含哪些问题&#xff1f; 1、基础问题&#xff0c;如斐波那契数 2、背包问题&#xff0c;很经典的问题 3、打家劫舍 4、…

安全开发实战(1)--Cdn

目录 安全开发专栏 CDN介绍 1.信息收集阶段 1.1判断CDN是否存在 1.1.1, One 1.1.2,Two(改进) 1.1.3,进行整合 增加输入功能 1.1.4 批量读取监测存储(进行测试) 问题1: 问题2: 解决方案: 1.1.4 基本编写完成 命令框中: cdn存在.txt 总结 这里我是根据整个渗透测…

C语言数据结构-双链表

双链表是什么 双向链表&#xff0c;又称为双链表&#xff0c;是链表的一种&#xff0c;它的每个数据结点中都有两个指针&#xff0c;分别指向直接后继和直接前驱。所以&#xff0c;从双向链表中的任意一个结点开始&#xff0c;都可以很方便地访问它的前驱结点和后继结点。 我们…

使用mapinfo软件的在线地图插件运行错误解决

使用mapinfo软件的在线地图插件运行错误解决 一、如何解决win10/win11家庭版运行MapInfo中的在线地图插件报错【unexpected error&#xff1b;quitting】问题&#xff1f;二、如何解决在线地图切换地图源时的报错问题&#xff1f; 一、如何解决win10/win11家庭版运行MapInfo中的…

原牛角源码(修罗bbs)全站程序打包带数据库备份

原牛角源码(修罗bbs)全站程序打包带数据库备份&#xff0c;牛角源码全站数据全站文件、插件打包分享给大家&#xff0c;有兴趣的可以搭建玩玩&#xff01; conf文件夹中自己配置conf.php里面的数据库链接文件&#xff0c;默认管理账号&#xff1a;admin&#xff0c;密码&#…

Redis的单线程模型解析与应用实践

引言 Redis作为一个高性能的键值存储数据库&#xff0c;其单线程模型一直是技术社区讨论的热点。本文将深入剖析Redis的单线程模型&#xff0c;探讨其背后的设计哲学、实现机制以及在实际应用中的最佳实践。 Redis单线程模型概述 Redis的单线程模型指的是其核心功能&#xf…

【SAP HANA 26】视图(Views)

创建视图及注释 -- 用租户DB1的NEWUSER操作 create view newuser.zv_student as select zno as student_no, zname as student_name,zsex as student_sex,zage as student_age from newuser.zstudent; --增加视图注释 comment on view newuser.zv_student is 学生信息视图; c…