【社区投稿】自动特征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
会自动扩散至它们的紧上一层数据结构:
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
限定条件,因为这条扩散链条的存在:

再举个更复杂的例子,假设有如下枚举类
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)
免责声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。工具来自网络,安全性自测,如有侵权请联系删除。本次测试仅供学习使用,如若非法他用,与平台和本文作…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究
摘要:在消费市场竞争日益激烈的当下,传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序,探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式,分析沉浸式体验的优势与价值…...