【社区投稿】自动特征auto trait的扩散规则

news2024/12/28 3:41:33

自动特征auto trait的扩散规则

公式化地概括,auto trait = marker trait + derived trait。其中,等号右侧的markerderived是在Rustonomicon书中的引入的概念,鲜见于Rust References。所以,若略感生僻,不奇怪。

marker traitderived trait精准概括了auto trait功能的两面性

  1. 前者指明auto trait实现类具备了由rustc编译器和std标准库对其约定的“天赋异能 intrinsic properties”。

  2. 后者描述了这些“天赋异能”沿auto trait实现类数据结构【自内及外】的继承性与扩散性。

接下来逐一解释。

marker trait标识“天赋”特征是什么

既然是“天赋”,那么auto trait没有任何抽象成员方法待被“后天实现”或关联项待被“后天赋值” — 这也是marker trait别名的由来。rustc甚至未配备专项检查器以静态分析与推断 @Rustacean 对auto trait的实现是否合理。类似于unsafe块,@Rustacean 需向rustc承诺:知道自已正在干什么,和提交可供其它程序模块信任的“天赋异能“代码实现。否则,运行时程序就会执行出未定义行为 U.B.。相比于传统的std程序接口和rustc内存安全承诺,这是一项“反向契约” — 即,由rustc充当“甲方”和规划功能要求,而由 @Rustacean 充当“乙方”完成功能代码和提供正确性保证。在硕大的Rust标准库中,这类“天赋异能“的”反向契约”并不多见,但包括

536b7280071b9e5f8fa4dab61194f88e.png

输入图片说明

derived trait明确“天赋”特征如何扩散

概括起来,auto trait的扩散规则就四个字“由内及外”。其遇到不同的场景,伴有不同解释的扩散链条

场景一:变量 ➜ 指针

以变量的数据类型为内,和以指向该变量值的指针/引用为外 — 变量值(数据类型T)实现的auto trait会自动扩散至它的各类指针与引用:

  • &T

  • &mut T

  • *const T

  • *mut T

所以,该扩散链条也被记作【类型 ➜ 指针】。

场景二:字段 ➜ 结构体

以字段的数据类型为内,和以父数据结构为外 — 所有字段(数据类型)都实现的auto trait会自动扩散至它们的紧上一层数据结构:

  • structs

  • enums

  • unions

  • tuples

所以,该扩散链条也被记作【字段 ➜ 结构】。

场景三:元素 ➜ 集合

以集合元素的数据类型为内,和以集合容器为外 — 由元素(数据类型T)实现的auto trait会自动扩散至该元素的紧上一层集合容器:

  • [T; n]

  • [T]

  • Vec<T>

场景四:捕获变量 ➜ 闭包

以捕获变量的数据类型为内,和以闭包为外 — 所有捕获变量(数据类型)都实现的auto trait会自动扩散至引用(或所有权占用)这些捕获变量的闭包。

场景五:函数 ➜ 函数指针

函数项fn与函数指针fn ptr总是会被rustc编译时自动实现全部auto trait

扩散链条的“串连”

前四个场景的扩散链是可以多重嵌套衔接的。举个例子,Vec<Wrapping<u8>>一定满足trait Send限定条件,因为这条扩散链条的存在:

575ab6313829c47c199ebdb53458f90e.png
输入图片说明

再举个更复杂的例子,假设有如下枚举类

enum Test<'a> {
    Str(&'a String),
    Num(u8)
}

那么Vec<Test>也一定满足trait Send限定条件,因为此扩散链条的存在:

2f123f967f6e9da64b11bc4a6dabb241.png
输入图片说明

auto trait扩散链条的“阻断”

  1. 安装nightlyrustc编译器。然后,在代码中,

  2. 开启#![feature(negative_impls)]feature-gate编译开关

  3. 否定实现auto trait。比如,impl !Unpin for Test {}

于是,rustc就不会再对遇到的【类型定义】自动添加曾被否定实现过的auto trait了。在众多auto trait中,仅trait Unpin绕过nightly编译工具链的依赖和仅凭stable标准库内符号类型std::marker::PhantomPinned定义的幻影字段阻止rustc悄悄地实现自动特征。

【幻影字段】是仅作用于编译时的零成本抽象项。它被用来帮助编译器理解 @Rustacean 提交的代码和推断 @Rustacean 的程序设计意图。其语义功能很像typescript中的【@ 装饰器】。即,

  1. 辅助代码静态分析

  2. 辅助编译器生成垫片程序

  3. 编译后立即抹除

  4. 对【运行时】不可见 — 这也是【零成本】的由来。但,世间任何事物都有两面性和是双刃剑。“零成本”是省CPU,但更费脑细胞呀!Rust编程的心智成本高已是行业共识了。

另值一提的是,【Rust幻影字段】与【typescript装饰器】皆都不同于【Java的 @ 注释】,因为

  • 前者是给编译器看和解读的 — 充其量是代码正文的旁白注脚。

  • 后者是给运行时VM用和执行的 — 这已算是正文指令的一部分了。

它们就是两个不同“位面”的东西。

我日常仅用过std::marker::PhantomPinnedstd::marker::PhantomData两类幻影字段

  • 前者解决stable编译工具链对trait Unpin的否定实现 — 就是这里正在讲的事

  • 后者被用于“类型状态Type State Pattern”设计模式中,将【运行时】对象状态的(动态)信息编码入【编译时】对象类型的(静态)定义里,以扩展Rust类型系统的应用场景至对状态集的“状态管理”。若您对“类型状态”设计模式有兴趣,推荐移步至我的另一篇主题文章对照 OOP 浅谈【类型状态】设计模式保证有收获。

这闲篇扯远了,让咱们重新回到文章的主题上来。

请细读下面自引用结构体的类型定义(特别含注释内容)和体会std::marker::PhantomPinned如何被用来声明结构体内的幻影字段:

use ::std::marker::PhantomPinned;
struct SelfReferential {
    // 整个结构体内唯一包含了有效信息的字段。
    a: String,
    // 自引用前一个字段`a`的值。
    ref_a: *const String,
    // 1. 这是对【幻影字段】的定义
    // 2. 因为该字段并不会真的被后续功能代码用到,
    //    所以为了压制来自编译器的`useless field`警告,
    //    字段名以`_`为前缀
    _marker: PhantomPinned 
} // 于是,自引用结构体`Test`就是 !Unpin 的了

即便不太理解,也请不要质疑【自引用结构体】的存在必要性。至少在异步程序块中,跨.await轮询点的变量引用都依赖这套机制。仅因为async {}语法糖把每次构造trait Future实现类的细节都隐藏了起来,所以 @Rustacean 对自引用结构体的直观感受会比较弱。

auto trait扩散不至的自定义数据结构

若数据结构定义内含有实现auto trait的字段(比如,

use ::std::env::Vars;
struct Dumb(Vars);

其中Dumb.0字段Vars明确是!Send的),那么 @Rustacean 就有必要考虑

  • 要么,重新规划程序设计,以规避要求struct Dumb满足trait Send限定条件

  • 要么,给struct Dumb添加unsafeauto trait实现块。

    unsafe impl Send for Dumb {};

后者的unsafe impl语法前缀就是rustc对程序作者最后的警告:“你真的明白,你正在做什么事吗?”。

【快排序】 综合例程

先贴源码,再做详解。敲黑板强调:代码内的注释同样重要呀!推荐同正文一样重视和仔细阅读。

fn quick_sort<T: Ord + Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }
    let mid = {
        let pivot = v.len() - 1;
        let mut i = 0;
        for j in 0..pivot {
            if v[j] <= v[pivot] {
                v.swap(i, j);
                i += 1;
            }
        }
        v.swap(i, pivot);
        i
    };
    // 1. 此处,虽然 lo 与 hi 是两个崭新的【切片】胖指针实例,
    //    但由【切片】胖指针引用的底层 Vec<i32> 数据值却只有
    //    一份呀!
    let (lo, hi) = v.split_at_mut(mid);
    // 2. 所以,后续的【多线程+递归】是修改的同一个 Vec<i32> 
    //    实例。
    rayon::join(|| quick_sort(lo),
                || quick_sort(hi));
    // 3. 至此,虽然此函数没有返回值,但仍可沿函数的【输入输出】
    //    实参 v: &mut [T] 向调用端传递排序结果。
}
fn main() {
    use ::rand::prelude::*;
    // 1. 生成一个大数字集合
    let mut numbers: Vec<i32> = (1000..10000).collect();
    // 2. 乱序数组内容
    numbers.shuffle(&mut rand::thread_rng()); 
    // 3. 多线程快排序
    quick_sort(&mut numbers); 
    // 4. 打印排序结果
    println!("Sorted: {:?}", &numbers[..10]);
    // 5. 一个单例 Vec<i32> 对象贯穿整个多线程快排序 demo 始终。
}

上述例程的难点并不是rayon::join()如何在后台悄悄唤起多个线程加速大数据集【快排序】 — 这不需要 @Rustacean 操心,咱们只要多读读 API 文档和了解Work Stealing工作原理就足够了。相反,曾经困惑过我一段时间的痛点是:

为什么虽然泛型类型参数<T: Ord + Send>的书面语义是“跨线程·数据复制”但程序的实际执行结果却是对Vec<i32>单例的“跨线程·内存共享”?即,多个线程透过【切片】胖指针,修改同一个Vec<T>变长数组实例。

推导明白这条逻辑链(元素Send ➜ 集合Sync)先后耗费了我不少心神。但总结起来也无非如下几步:

  1. 依据前文介绍的auto trait扩散规则,对特征trait Send和泛型类型参数T,构造初始扩散链条:

    75d06a4e7b6f1f88522d6db13caa0219.png
    输入图片说明
  2. 依据trait Sendtrait Sync的(单向)转换关系,有<&S: Send> → <S: Sync>

  3. 将 #2 代入 #1,进一步完善trait Send扩散链条,有

    d70179ea7fa2cc2e7277867d58e8ba66.png
    输入图片说明
  4. 依据rustc赋予trait Sync的语义,集合[T]被允许跨线程引用与多线程共享

  5. 又因为fn quick_sort()的形参是对Vec<i32>实例的可修改引用&mut,所以多个线程被允许并行修改同一个变长数组实例。

你不会以为故事就此结束了吧?难道你没有发觉例程中多线程代码有缺了点儿什么的异样吗?没错!细心的读者可能早就想问:

对单实例变长数组的并行修改,为什么未采用【读写锁RwLock】或【互斥锁Mutex】加以同步保护呢?甚至rustc在编译时连警告提示都没有输出?What's wrong?

好问题!您有心了。快速回答是:虽然Vec<i32>实例同时被多个线程并行修改不假,但每个线程并行修改的切片却只是同一变长数组内彼此衔接却并不相交的“子段”。所以,在快排序过程中,事实上没有任何数据竞争发生 — 这是彻头彻尾的算法胜利。果真,编程的尽头是数学与算法啊!此外,rustc能顺利地接受与成功地编译这样的代码也足已破除人们以往对它保守且不变通的刻板印象。

结束语

这次就先分享这一个小知识点。文章里埋的有关Unpin的坑以后再填。2024搞了一年的“鸿蒙Next ArkTs”真不容易。哎!天大地大,饭辙最大。我是一颗螺丝钉,甲方爸爸需要什么,我就研究什么。但,年底写篇Rust知识分享文章压压惊。

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

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

相关文章

【Go】context标准库

文章目录 1. 概述1.1 什么是 Context1.2 设计原理1.3 使用场景1.4 Context 分类核心:Context接口2. 源码解读4个实现emptyCtxTODO 和 BackgroundcancelCtxWithCancelcancelCtx.propagateCancel 构建父子关联parentCancelCtx 获取父上下文中的内嵌cancelCtxcanceltimerCtxWithT…

【视觉惯性SLAM:四、相机成像模型】

相机成像模型介绍 相机成像模型是计算机视觉和图像处理中的核心内容&#xff0c;它描述了真实三维世界如何通过相机映射到二维图像平面。相机成像模型通常包括针孔相机的基本成像原理、数学模型&#xff0c;以及在实际应用中如何处理相机的各种畸变现象。 一、针孔相机成像原…

CAPL_构建基于UDS的刷写学习—04 思路的构建_第一部分

前言与导读&#xff1a; 基于前几篇文章我们已经梳理了HEX文件、S19文件的读取和UDS关键的0x34/0x36/0x37等服务的结构。 基于此&#xff0c;我们差不多就完成了前期的知识储备了&#xff0c;那么完成最终的目的——使用capl实现我们还需要解决以下几个问题。 1、文件如何读…

分布式 IO 模块助力冲压机械臂产线实现智能控制

在当今制造业蓬勃发展的浪潮中&#xff0c;冲压机械臂产线的智能化控制已然成为提升生产效率、保障产品质量以及增强企业竞争力的关键所在。而分布式 IO 模块的应用&#xff0c;正如同为这条产线注入了一股强大的智能动力&#xff0c;开启了全新的高效生产篇章。 传统挑战 冲压…

香橙派5Plus启动报错bug: spinlock bad magic on cpu#6, systemd-udevd/443

一、问题 如图&#xff1a; 接上调试串口&#xff0c;每次启动都会报错。不过使用过程中没有发现有什么影响。 百度查阅&#xff0c;有一位博主提到&#xff0c;但是没有细说解决方案&#xff1a; spinlock变量没有初始化_spinlock bad magic on-CSDN博客https://blog.csdn.n…

Matrix-Breakout 2 Morpheus(找到第一个flag)

第一步 信息收集 (1)寻找靶场真实ip arp-scan -l 靶场真实 ip 为192.168.152.154 (2)探测端口及服务 nmap -p- -sV 192.168.52.135 第二步 开始渗透 (1)访问web服务 http://192.168.152.154and http://192.168.52.135:81 发现 81 端口的页面要登录 我们使用 dirb 扫描…

学习C++:关键字

关键字&#xff1a; 作用&#xff1a;关键字是C预先保留的单词&#xff08;标识符&#xff09; 在定义变量或者常量时候&#xff0c;不要用关键字 不要用关键字给变量或者常量起名称

Android笔记(四十):ViewPager2嵌套RecyclerView滑动冲突进一步解决

背景 ViewPager2内嵌套横向滑动的RecyclerView&#xff0c;会有滑动冲突的情况&#xff0c;引入官方提供的NestedScrollableHost类可以解决冲突问题&#xff0c;但是有一些瑕疵&#xff0c;滑动横向RecyclerView到顶部&#xff0c;按住它不放手继续往左拖再往右拖&#xff0c;这…

【提审】Android包提审报权限问题

问题&#xff1a;华为应用市场审核不通过 平台审核检测详情&#xff1a; 日志&#xff1a; 自检工具&#xff1a;frida-server【Unity&Android】安卓app自测应用隐私相关获取和申请权限_apk 隐私合规 自测-CSDN博客 参考资料&#xff1a;Unity启动时获取了android_id等设…

QtQuick之QML应用程序开:一、使用资源文件以及给应用程序添加图标

开发环境: 1、Qt Creator 14.0.1 2、windows10 先看下面的步骤,不明白再返回来看下面官方指导链接。 先看下面的步骤,不明白再返回来看下面官方指导链接。 先看下面的步骤,不明白再返回来看下面官方指导链接。 --------------------------------------------------------…

Task :prepareKotlinBuildScriptModel UP-TO-DATE,编译卡在这里不动或报错

这里写自定义目录标题 原因方案其他思路 原因 一般来说&#xff0c;当编译到这个task之后&#xff0c;后续是要进行一些资源的下载的&#xff0c;如果你卡在这边不动的话&#xff0c;很有可能就是你的IDE目前没有办法进行下载。 方案 开关一下IDE内部的代理&#xff0c;或者…

webauthn介绍及应用

1、webauthn介绍 官网&#xff1a;https://webauthn.io/ 1.1、什么是webauthn&#xff1f; webauthn即Web Authentication&#xff0c;是一个符合W3C标准的Web认证规范。它通过公私钥加密技术&#xff0c;实现无密码认证&#xff0c;用户仅需通过pin码、指纹、面部识别、usb …

中文学习系统:成本效益分析与系统优化

2.1 SSM框架介绍 本课题程序开发使用到的框架技术&#xff0c;英文名称缩写是SSM&#xff0c;在JavaWeb开发中使用的流行框架有SSH、SSM、SpringMVC等&#xff0c;作为一个课题程序采用SSH框架也可以&#xff0c;SSM框架也可以&#xff0c;SpringMVC也可以。SSH框架是属于重量级…

centos单机部署seata

文章目录 场景分析下载seata包启动 场景 centos7.9 jdk17 安装部署seata 分析 jdk和seata的版本对应关系如图 JDK版本 推荐 Seata 版本 理由 JDK 8 任何 Seata 版本 JDK 8 是 Seata 长期支持的版本&#xff0c;兼容性最好。 JDK 11 Seata 1.2.0 适合需要长期支持且性能较高的应…

若依前端挂Nginx、打包部署运行!!!!

先了解知识&#xff1a; const proxy require(http-proxy-middleware);module.exports { devServer:{host: localhost, //target hostport: 8080,//proxy:{/api:{}},代理器中设置/api,项目中请求路径为/api的替换为targetproxy:{/api:{target: http://192.168.1.30:8085,/…

Vue CLI 3 项目构建

Vue CLI 是一个功能强大、易于使用的工具&#xff0c;可以极大地简化 Vue.js 应用的开发过程。通过快速创建项目、灵活的插件系统和丰富的配置选项&#xff0c;开发者可以更专注于业务逻辑&#xff0c;而不是底层配置。无论是新手还是经验丰富的开发者&#xff0c;Vue CLI 都是…

电脑提示报错NetLoad.dll文件丢失或损坏?是什么原因?

一、NetLoad.dll文件丢失或损坏的根源 程序安装不完整&#xff1a;某些程序在安装过程中可能因为磁盘错误、网络中断或安装程序本身的缺陷&#xff0c;导致NetLoad.dll文件未能正确安装或复制。 恶意软件攻击&#xff1a;病毒、木马等恶意软件可能会篡改或删除系统文件&#x…

SpringBoot(二)—— yaml配置文件

接上篇&#xff0c;我们对SpringBoot有了基本的了解&#xff0c;接下来探究配置文件。 目录 二、配置文件 1. SpringBoot热部署 2. 配置文件 2.1 配置文件的作用 2.2 YAML 配置文件 2.3 YAML 与 XML 比较 3. YAML语法 3.1 键值对 3.2 值的写法 3.3 对象/Map&#x…

基于PyQt5的UI界面开发——多界面切换

介绍 最初&#xff0c;因为课设的缘故&#xff0c;我只是想做一个通过按键进行切面切换而已&#xff0c;但是我看网上资料里面仅是语焉不详&#xff0c;让我困惑的很&#xff0c;但后面我通过摸索才发现这件事实在是太简单了&#xff0c;因此我想要记录下来。 本博客将介绍如…

Virtualbox硬盘扩容

前言 有没有使用虚拟机安装操作系统的时候&#xff0c;虚拟硬盘一开始分配的虚拟硬盘空间不够用&#xff1f;在后期去扩容的伙伴们&#xff0c;下面我看看如何扩容virtualbox的虚拟硬盘&#xff1f; 重新分配虚拟硬盘大小 在virtualbox菜单选择【管理】-【工具】-【虚拟介质…