【社区投稿】自动特征auto trait的扩散规则
自动特征auto trait的扩散规则
公式化地概括,auto trait = marker trait + derived trait。其中,等号右侧的marker与derived是在Rustonomicon书中的引入的概念,鲜见于Rust References。所以,若略感生僻,不奇怪。
marker trait与derived trait精准概括了auto trait功能的两面性
前者指明
auto trait实现类具备了由rustc编译器和std标准库对其约定的“天赋异能 intrinsic properties”。后者描述了这些“天赋异能”沿
auto trait实现类数据结构【自内及外】的继承性与扩散性。
接下来逐一解释。
marker trait标识“天赋”特征是什么
既然是“天赋”,那么auto trait就没有任何抽象成员方法待被“后天实现”或关联项待被“后天赋值” — 这也是marker trait别名的由来。rustc甚至未配备专项检查器以静态分析与推断 @Rustacean 对auto trait的实现是否合理。类似于unsafe块,@Rustacean 需向rustc承诺:知道自已正在干什么,和提交可供其它程序模块信任的“天赋异能“代码实现。否则,运行时程序就会执行出未定义行为 U.B.。相比于传统的std程序接口和rustc内存安全承诺,这是一项“反向契约” — 即,由rustc充当“甲方”和规划功能要求,而由 @Rustacean 充当“乙方”完成功能代码和提供正确性保证。在硕大的Rust标准库中,这类“天赋异能“的”反向契约”并不多见,但包括

derived trait明确“天赋”特征如何扩散
概括起来,auto trait的扩散规则就四个字“由内及外”。其遇到不同的场景,伴有不同解释的扩散链条
场景一:变量 ➜ 指针
以变量的数据类型为内,和以指向该变量值的指针/引用为外 — 变量值(数据类型T)实现的auto trait会自动扩散至它的各类指针与引用:
&T&mut T*const T*mut T
所以,该扩散链条也被记作【类型 ➜ 指针】。
场景二:字段 ➜ 结构体
以字段的数据类型为内,和以父数据结构为外 — 所有字段(数据类型)都实现的auto trait会自动扩散至它们的紧上一层数据结构:
structsenumsunionstuples
所以,该扩散链条也被记作【字段 ➜ 结构】。
场景三:元素 ➜ 集合
以集合元素的数据类型为内,和以集合容器为外 — 由元素(数据类型T)实现的auto trait会自动扩散至该元素的紧上一层集合容器:
[T; n][T]Vec<T>
场景四:捕获变量 ➜ 闭包
以捕获变量的数据类型为内,和以闭包为外 — 所有捕获变量(数据类型)都实现的auto trait会自动扩散至引用(或所有权占用)这些捕获变量的闭包。
场景五:函数 ➜ 函数指针
函数项fn与函数指针fn ptr总是会被rustc编译时自动实现全部auto trait。
扩散链条的“串连”
前四个场景的扩散链是可以多重嵌套衔接的。举个例子,Vec<Wrapping<u8>>一定满足trait Send限定条件,因为这条扩散链条的存在:
再举个更复杂的例子,假设有如下枚举类
enum Test<'a> {Str(&'a String),Num(u8)
} 那么Vec<Test>也一定满足trait Send限定条件,因为此扩散链条的存在:
auto trait扩散链条的“阻断”
安装
nightly版rustc编译器。然后,在代码中,开启
#![feature(negative_impls)]的feature-gate编译开关,否定实现
auto trait。比如,impl !Unpin for Test {}
于是,rustc就不会再对遇到的【类型定义】自动添加曾被否定实现过的auto trait了。在众多auto trait中,仅trait Unpin可绕过对nightly编译工具链的依赖和仅凭stable标准库内符号类型std::marker::PhantomPinned定义的幻影字段阻止rustc悄悄地实现自动特征。
【幻影字段】是仅作用于编译时的零成本抽象项。它被用来帮助编译器理解 @Rustacean 提交的代码和推断 @Rustacean 的程序设计意图。其语义功能很像
typescript中的【@ 装饰器】。即,
辅助代码静态分析
辅助编译器生成垫片程序
编译后立即抹除
对【运行时】不可见 — 这也是【零成本】的由来。但,世间任何事物都有两面性和是双刃剑。“零成本”是省
CPU,但更费脑细胞呀!Rust编程的心智成本高已是行业共识了。另值一提的是,【
Rust幻影字段】与【typescript装饰器】皆都不同于【Java的 @ 注释】,因为
前者是给编译器看和解读的 — 充其量是代码正文的旁白注脚。
后者是给运行时
VM用和执行的 — 这已算是正文指令的一部分了。它们就是两个不同“位面”的东西。
我日常仅用过
std::marker::PhantomPinned与std::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添加unsafe的auto 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)先后耗费了我不少心神。但总结起来也无非如下几步:
依据前文介绍的
auto trait扩散规则,对特征trait Send和泛型类型参数T,构造初始扩散链条:
输入图片说明 依据
trait Send至trait Sync的(单向)转换关系,有<&S: Send> → <S: Sync>。将 #2 代入 #1,进一步完善
trait Send扩散链条,有
输入图片说明 依据
rustc赋予trait Sync的语义,集合[T]被允许跨线程引用与多线程共享又因为
fn quick_sort()的形参是对Vec<i32>实例的可修改引用&mut,所以多个线程被允许并行修改同一个变长数组实例。
你不会以为故事就此结束了吧?难道你没有发觉例程中多线程代码有缺了点儿什么的异样吗?没错!细心的读者可能早就想问:
对单实例变长数组的并行修改,为什么未采用【读写锁RwLock】或【互斥锁Mutex】加以同步保护呢?甚至rustc在编译时连警告提示都没有输出?What's wrong?
好问题!您有心了。快速回答是:虽然Vec<i32>实例同时被多个线程并行修改不假,但每个线程并行修改的切片却只是同一变长数组内彼此衔接却并不相交的“子段”。所以,在快排序过程中,事实上没有任何数据竞争发生 — 这是彻头彻尾的算法胜利。果真,编程的尽头是数学与算法啊!此外,rustc能顺利地接受与成功地编译这样的代码也足已破除人们以往对它保守且不变通的刻板印象。
结束语
这次就先分享这一个小知识点。文章里埋的有关Unpin的坑以后再填。2024搞了一年的“鸿蒙Next ArkTs”真不容易。哎!天大地大,饭辙最大。我是一颗螺丝钉,甲方爸爸需要什么,我就研究什么。但,年底写篇Rust知识分享文章压压惊。
相关文章:
【社区投稿】自动特征auto trait的扩散规则
自动特征auto trait的扩散规则 公式化地概括,auto trait marker trait derived trait。其中,等号右侧的marker与derived是在Rustonomicon书中的引入的概念,鲜见于Rust References。所以,若略感生僻,不奇怪。 marker …...
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
要成为一名云原生相关的 Go 语言工程师,需要在 Go 语言、云原生技术栈以及相关的开发和运维工具上建立扎实的基础。下面是一个前字节员工总结的技术路线规划: 1. 掌握 Go 语言基础 深入理解 Go 语言:你需要熟练掌握 Go 的语法、数据结构、并…...
mui框架开发的手机APP——众筹约课类【只有前端,无后端】
点击获取源码...
Python的内存管理
文章目录 1. **内存管理的基本原理**(1)动态内存分配(2)引用计数机制 2. **垃圾回收(Garbage Collection, GC)机制**(1)循环引用问题(2)垃圾回收器的作用 3. …...
VSCode调试
目录 C/C远程本地调试插件配置参考 C/C远程本地调试 测试源码:https://github.com/jrhee17/ssl-study 插件 Remote - SSH C/C 配置 .vscode/launch.json {"version": "0.2.0","configurations": [{"name": "afte…...
Direct Preference Optimization (DPO) 简介与流程解析:中英双语
Direct Preference Optimization (DPO) 简介与流程解析 Direct Preference Optimization (DPO) 是一种基于人类偏好的强化学习优化方法,用于训练语言模型,使其更好地满足用户需求或偏好。本文将详细介绍 DPO 的核心思想、优化流程,并结合代码…...
fisco-bcos手动搭建webase启动注意事项
手动搭建webase-front启动注意事项 Java环境变量:1.8.301时候的错误 一直提示节点连接不上,无法连接chanale端口 这是官方提供的解决办法Help wanted: solution for secp256k1 being disabled Issue #470 FISCO-BCOS/java-sdk Java SDK 2.x连接节点失败…...
ospf 的 状态机详解
OSPF(开放最短路径优先,Open Shortest Path First)协议的状态机是其核心部分之一,用于确保路由器之间的邻接关系(neighbor relationship)建立和路由信息的交换。OSPF的状态机模型由多个状态组成,…...
TP5 动态渲染多个Layui表格并批量打印所有表格
记录: TP5 动态渲染多个Layui表格每个表格设置有2行表头,并且第一行表头在页面完成后动态渲染显示内容每个表格下面显示统计信息可点击字段排序一次打印页面上的所有表格打印页面上多个table时,让每个table单独一页 后端代码示例: /*** Nod…...
spring专题笔记(六):bean的自动装配(自动化注入)-根据名字进行自动装配、根据类型进行自动装配。代码演示,通俗易懂。
目录 一、根据名字进行自动装配--byName 二、根据类型进行自动装配 byType 本文章主要是介绍spring的自动装配机制, 用代码演示spring如何根据名字进行自动装配、如何根据类型进行自动装配。代码演示,通俗易懂。 一、根据名字进行自动装配--byName Us…...
监听器listener
文章目录 监听器( listener)对Application内置对象监听的语法和配置对session内置对象监听的语法和配置 监听器( listener) 对象与对象的关系: 继承关联 tomcat一启动创建的顺序:监听器,config,application(全局初始化参数)&am…...
重温设计模式--10、单例模式
文章目录 单例模式(Singleton Pattern)概述单例模式的实现方式及代码示例1. 饿汉式单例(在程序启动时就创建实例)2. 懒汉式单例(在第一次使用时才创建实例) 单例模式的注意事项应用场景 C代码懒汉模式-经典…...
Flutter动画学习二
如何在 Flutter 中使用自定义动画和剪裁(clipping)实现一个简单的动画效果。 前置知识点学习 AnimationController AnimationController 是 Flutter 动画框架中的一个核心类,用于控制动画的生命周期和状态。它提供了一种灵活的方式来定义动…...
讯飞语音听写WebApi(流式)【React Native版】
假设已有 Base64 编码的音频文件(16kHz, s16le, pcm) 1、获取websocket url import * as CryptoJS from crypto-js;/*** 获取websocket url*/ const getWebSocketUrl () > {const config {// 请求地址hostUrl: "wss://iat-api.xfyun.cn/v2/iat",host: "i…...
【Linux编程】一个基于 C++ 的 TCP 客户端异步(epoll)框架(一))
TcpClient 类的设计与实现:一个基于 C 的 TCP 客户端框架 在现代网络编程中,TCP(传输控制协议)客户端是实现网络通信的基础组件之一。本文将详细介绍一个基于 C 的 TcpClient 类的设计与实现,该类提供了创建 TCP 连接…...
PG备份恢复--pg_dump
pg_dump pg_dump 是一个逻辑备份工具。使用 pg_dump 可以在数据库处于使用状态下进行一致 性的备份,它不会阻塞其他用户对数据库的访问 。 一致性备份是 pg_dump 开始运行时,给数据库打了一个快照,且在 pg_dump 运行过程 中发生的更新将不会被备份。 …...
pikachu靶场搭建详细步骤
一、靶场下载 点我去下载 二、靶场安装 需要的环境: mysqlApaches(直接使用小皮面板Phpstudy:https://www.xp.cn/),启动他们 设置网站,把靶场的路径对应过来 对应数据库的信息 由于没有核对数据库的信…...
HarmonyOS NEXT开发进阶(五):装饰器讲解
一、Provide Consume 父组件与子组件的子组件(官方叫法:后代组件)双向同步数据(即,父组件与后代组件可以相互操作 Provide 修饰的数据) 注意:Provide 与 Consume声明的变量名必须一致。 import {TestChild } from .…...
【编译原理】往年题汇总(山东大学软件学院用)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀编译原理_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...
【漏洞复现】F5 BIG-IP Next Central Manager SQL注入漏洞(CVE-2024-26026)
免责声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。工具来自网络,安全性自测,如有侵权请联系删除。本次测试仅供学习使用,如若非法他用,与平台和本文作…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...
