[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?

这篇具有很好参考价值的文章主要介绍了[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?

在阅读各类Rust英文技术资料时,你是否也曾经困惑过:为何每逢【枚举值】的概念出现时,作者都会以variant一词指代之?就字面含义而言,enum value岂不是更贴切与易理解。简单地讲,这馁馁地是Rust技术优越性·宣传软文的广告梗,而且是很高端的内行梗。Rustacean们看了往往报以会心一笑 — 似乎优秀尽在不言中。至于梗在何处,请耐心听我娓娓道来!

在C++语境下,variant意味着什么

首先,当variant被记作variant member时,根据C++ 11标准,它指的就是C union数据结构中的字段。C union允许在同一段内存上,每次保存数据类型不同的值。但在程序运行期间,C union却并不支持内省·窥知它正保存哪种类型的值。程序员需要自己记忆这些代码细节与保持读写类型的一致。variant的字面含义“变异体”也贴切地暗示了该语法项的“飘忽不定,与琢磨不透”。

[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?

其次,当variant被记作std::variant时,根据C++ 17标准,它便是Tagged Union的语法糖。比如,std::variant就支持运行时“穷举”找出正被用来保存数据的“活跃”类型。例程1核心代码如下:

// 声明一个`int`, `float`与`std::string`类型的【共用体】。
std::variant<int, float, std::string> intFloatString;
// 初始化为`float`值。
intFloatString = 100.0f;
// 开始穷举该【共用体】,以找出它正保存什么类型的值
if (std::holds_alternative<int>(intFloatString))
    std::cout << "正在保存整数!\n";
else if (std::holds_alternative<float>(intFloatString))
    std::cout << "正在保存浮点数\n"; // 程序执行后,这一条日志将被输出
else if (std::holds_alternative<std::string>(intFloatString))
    std::cout << "正在保存字符串\n";

个人观点,std::variant最多算是对飙Rust enum语言核心特性的C++标准库实现版本。请注意被加粗的两个关键词“语言核心”与“标准库”。所以,即便Rust enumstd::variant功能相同,前者也是发自语言内核的“天赋技能”,而后者仅是来自标准库的“后天补丁”。和人一样,“先天就聪明”与“后天人为训练”是两码事!他们的成长上限不同,文章后续会再有提到。

Rust Union并没有带来新改善

为了优化互操作性,Rust也有与C union概念对等的数据结构Union。但程序对Rust Union实例任何读操作都是unsafe的,因为rustc不能编译时保证对相同Union实例的任何一次读写操作都采用了正确的数据类型。例如,对同一个Rust Union实例,先用f32写,再以String读就会导致程序panic,因为f32字节序列不符合UTF-8编码规则例程2。真是“十步一Crash,五步一UB (i.e. Undefined Behavior)”呀!难!

[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?

此外,对称于C union实例在切换至活跃“字段”时会自动释放“活跃”字段的内存占用,rustc直接禁止Drop trait实现类(比如,String)作为Rust Union数据结构的成员字段(,除非该字段被显示地标定为内存自理)例程3。这简单粗暴的作法也真的没谁了!

Rust enum带来的创新

enum valueC/Cpp语法规则中只能是intunsigned int类型。即便是在语法限制更为宽松的计算机语言中,enum value至少也得是数据类型相同的值。但,在Rust中,一个枚举类enum不同枚举值enum value被允许存储类型不同的数据。于是,

  • 相比于C unionRust enum包含分辨因子discriminant和支持(穷举)匹配。经由match表达式(穷举)匹配全部枚举值,程序必定能找到正确的数据类型读取enum枚举项内的值。

  • 相较于C enum

    • Rust enum能够在每个枚举值enum value内保存不同类型的值。

    • 更重要的是,当Rust enum实例切换到枚举值时,枚举值内保存对象的析构函数Drop::drop(&mut self)会被自动调用和释放内存例程4。这兑现了rustc的内存安全承诺。

因此,Rust enumC enumC union的概念集合体(即,Rust enum = C enum+ C union)。更准确地讲,Rust enum = C struct + C enum+ C union。其中,

  • C struct作为容器,起到了收拢命名空间的作用。

  • C enum记忆正保存哪个枚举值

  • C union存储不同类型具体的值

[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?

虽然Rust enum在功能上无限接近于C++ 17标准库中的std::variant数据结构,但Rust enum更高级,因为Rust enum语言内核特性,而不是来自标准库的后天补丁(数据结构)。于是,即便为了适配硬件条件简陋的嵌入式设备,我们不得不开启#![no_std]模式和弃掉整个【标准库】

  • Rust程序依旧安全、精简和高性能。

  • C++程序员必定要为重构代码而哭晕在工位上。

综上所述,将枚举值记作variantRust团队向全世界潜移默化地输出Rust enum技术优越性观念的手段。即,

  • 兼容多类型 — 同C union,和引入variant别名。

  • 可穷举匹配 — 同C enum

  • 自动析构旧值 — Rust内存安全保证

  • 语言内核支持 — Rust对要求弃掉【标准库】的嵌入式编程友好

然后,再追问一句:“都这么好了,你还不来上手试试吗?”。这是多有技术格的广告梗呀!

当然,任何事物都有正反两面,既然Rust enum如此地特立独行,那么其它计算机语言应该如何布局内存来描述被FFI导入的Rust enum实例呢,又或许Rust enum就从此无缘FFI了?这注定不是轻松的工作。于是,才有了文章的最后一节:不怕,以C ABI为中间格式。

FFI导入Rust enum

【枚举类】从RustCABI映射关系决定了其它计算机语言(比如,nodejs)如何FFI导入Rust枚举值,因为Rust是以C ABI为中间协议实现跨语言互操作的。

ABI话题本身就是一个Esoteric Topic。在这里,我仅点到为止地聊两句:若Rust程序

  • 在编译时·经由rustc链接*.rlib链接库文件,那就采用Rust ABI实现互操作。

  • 在运行时·链接由【rustcdylib包类型】编译出的*.dll / *.dylib / *.so链接库文件,那也采用Rust ABI实现互操作。

    • Windows*.dll

    • Mac*.dylib

    • -nix*.so

  • 在运行时·链接非以上两种类型的任何链接库文件,那都采用C ABI实现互操作。所以,Rust与其它任何计算机语言都是经由C ABI协议联通的。

似乎文字还是缺乏描述力,那就承接上图的例子,观察nodejs如何FFI导入由Rust端输出的Result枚举值。就java / python / ruby而言,其底层原理也是一样。

[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?

简单地讲,就FFI的调用端来说,其只见structunion,而不见enum,因为Rust enum的穷举匹配能力被转变成了tag索引字段的整数比较操作。tag索引字段的

  • 字段名tag是硬编码的C ABI约定,改不了。

  • 字段值是始于0的整数。

  • 字段值大小反映的是Rust枚举值在声明时的词法次序。

最后,js调用端的完整代码如下

const [ffi, ref, Union, Struct] = ['ffi', 'ref', 'ref-union', 'ref-struct'].map(require);
const Result = Struct({ // 虽然完全看不出数据源是`enum`,但`Tagged Union`风却直扑面门。
    tag: ref.types.uint8,
    union: new Union({
        ciphertext: Struct({
            password: 'string',
            nonce: 'string'
        }),
        errCode: Struct({
            err_code: ref.types.uint8
        })
    })
});
const core = ffi.Library(dllPath, {
    calcNonce: ['string', []],
    encryptPassword: [Result, ['string', 'string']]
});
const result = core.encryptPassword('12222', core.calcNonce());
switch (result.tag) { // 枚举值匹配·转变成了·索引值比较
case 0: // 加密成功,和输出密文密码
    console.log('password=', result.union.ciphertext.password, 'nonce=', result.union.ciphertext.nonce);
    break;
case 1: // 加密失败,和输出错误码
default:
    console.log('err_code=', result.union.errCode.err_code);
    break;
}

结束语

前不久有网友私信我和热烈讨论了这个技术点。太有意义了!事后,我汇总·提炼聊天内容,和进一步做了概念延伸(于是,才有FFI一节)。最终,写下这篇文章与大家分享。希望那位网友看到这篇文章也能帮我点赞与发评论暖场。更请路过的神仙哥哥与仙女妹妹们指导与纠错,共同进步。谢谢!

哎,Rust是真难学,我好像又进入“瓶颈”状态了。头疼!文章来源地址https://www.toymoban.com/news/detail-402746.html

到了这里,关于[Rust笔记] 为什么Rust英文文档普遍将【枚举值】记作variant而不是enum value?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Elasticsearch 为什么会产生文档版本冲突?如何避免?

    先让大家直观的看到 Elasticsearch 文档版本冲突。 1.1 场景1:create 场景 1.2 场景2:批量更新场景模拟 模拟脚本1:循环写入数据 index.sh。 模拟脚本2:循环update_by_query 批量更新数据 update.sh。 由于:写入脚本 index.sh 比更新脚本 update.sh (执行一次,休眠1秒)执行要快,所以更新

    2023年04月08日
    浏览(48)
  • 【word密码】Word 文档设置了只读为什么还能编辑?

    有些朋友可能会遇到这种疑问,为什么我的Word文档设置了只读模式,还是可以编辑的,这是什么原因呢? 其实是因为大部分的只读模式,设置完成之后都是可以编辑的,但是当我们进行保存的时候就会发现,word提示需要重命名并选择新路径才能够保存。 这种操作,即使可以

    2024年01月25日
    浏览(70)
  • html5为什么只需要写<!doctype html>? 有多少种Doctype文档类型?

    HTML5只需要写!doctype html是因为HTML5不基于SGML,不需要对DTD进行引用,但仍需要doctype来规范浏览器的行为。而HTML4.01基于SGML,需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。 Doctype文档类型是指用于标识HTML或XML文档类型的声明,它告诉浏览器文档所使用的规范或标

    2024年01月20日
    浏览(82)
  • 笔记:TCP握手为什么是3次而不是2次?

    这个问题比较常见,这里简单总结一下。 一、两次握手建立连接:流程说明: 1)客户端发送SYN。 2)服务端收到SYN请求后,服务端回复SYN+ACK,然后进入已连接状态。 3)客户端收到SYN+ACK回复后,进入已连接状态。 二、两次握手建立连接:存在的问题 若客户端发送SYN后,没

    2023年04月13日
    浏览(58)
  • 学习笔记18——个人理解为什么快速重传是3次ACK

    为什么快速重传是选择3次ACK? 个人理解:首先网络中的丢包,乱序以及网路故障都会让服务器端发回duplicated ACK,表示有一个包一直未收到。快速重传是通过3次ACK来区分乱序,丢包和网路拥塞的情况,是基于实践经验得到的,所以说这种判断方式不一定准确,只是“大概率

    2024年02月02日
    浏览(41)
  • 小红书没人点赞是为什么?如何提升笔记点赞

    随着小红书规模的逐渐扩大,活跃用户的数量逐渐增多,也会让很多人产生一种疑问:为什么月活用户那么多,但是自己的小红书没人点赞。今天就来和大家一起谈谈这个问题,小红书没人点赞是为什么,有哪些情况会造成这种现象? 一、哪些情况造成小红书没人点赞这样一

    2023年04月15日
    浏览(50)
  • 【3B1B笔记】e的矩阵指数——怎么算?为什么?

    【【官方双语】e的矩阵指数——怎么算?为什么?】 注:本文未记录薛定谔方程及量子力学部分 把不同的式子带入泰勒级数,记作e的指数 对于矩阵的式子来说,为矩阵的乘方和加减运算,但对是否可以推广到无穷存疑 将矩阵 带入该级数,取极限后趋近于 ,差不多是(-1)×单

    2024年02月05日
    浏览(53)
  • 为什么阿里人能够快速成长?看完他们 Java 架构进化笔记,我秒懂!

    0-1 年入门: Java 基础复盘 (面向对象+Java 的超类+Java 的反射机制+异常处理+集合+泛型+基础 IO 操作+多线程+网络编程+JDK 新特性) Web 编程初探 (Servlet+MySQL 数据库+商品管理系统实战) SSM 从入门到精通 (Spring+SpringMVC+Mybatis+商品管理系统实战-SSM 版) SpringBoot 快速上手 (Spr

    2023年04月19日
    浏览(58)
  • (学习笔记-TCP连接建立)TCP 为什么是三次握手?不是两次、四次?

    常规回答:“因为三次握手才能保证双方具有接收和发送的能力” 三次握手的 首要原因是为了防止旧的重复连接初始化造成混乱 。 假设:客户端先发送了SYN(seq=90)报文,然后客户端宕机了,而且这个SYN报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向

    2024年02月17日
    浏览(50)
  • 惠普电脑/笔记本电脑为什么左下角会出现 按 ESC 进入启动选项,怎么关闭

    惠普电脑/笔记本电脑为什么左下角会出现 按 ESC 进入启动选项,怎么关闭 查看: 现象提示图片示例: ![v 出现原因 出现这个提示,说明你关闭了 『安全启动』,开启了 『传统模式』 解决方法 首先确定你的硬盘分区是 『MBR 』还是 『GPT』,如果你的『引导盘/系统盘分区』

    2024年02月10日
    浏览(175)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包