当前位置: 首页 > news >正文

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

自动特征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知识分享文章压压惊。

相关文章:

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

自动特征auto trait的扩散规则 公式化地概括&#xff0c;auto trait marker trait derived trait。其中&#xff0c;等号右侧的marker与derived是在Rustonomicon书中的引入的概念&#xff0c;鲜见于Rust References。所以&#xff0c;若略感生僻&#xff0c;不奇怪。 marker …...

云原生相关的 Go 语言工程师技术路线(含博客网址导航)

要成为一名云原生相关的 Go 语言工程师&#xff0c;需要在 Go 语言、云原生技术栈以及相关的开发和运维工具上建立扎实的基础。下面是一个前字节员工总结的技术路线规划&#xff1a; 1. 掌握 Go 语言基础 深入理解 Go 语言&#xff1a;你需要熟练掌握 Go 的语法、数据结构、并…...

mui框架开发的手机APP——众筹约课类【只有前端,无后端】

点击获取源码...

Python的内存管理

文章目录 1. **内存管理的基本原理**&#xff08;1&#xff09;动态内存分配&#xff08;2&#xff09;引用计数机制 2. **垃圾回收&#xff08;Garbage Collection, GC&#xff09;机制**&#xff08;1&#xff09;循环引用问题&#xff08;2&#xff09;垃圾回收器的作用 3. …...

VSCode调试

目录 C/C远程本地调试插件配置参考 C/C远程本地调试 测试源码&#xff1a;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) 是一种基于人类偏好的强化学习优化方法&#xff0c;用于训练语言模型&#xff0c;使其更好地满足用户需求或偏好。本文将详细介绍 DPO 的核心思想、优化流程&#xff0c;并结合代码…...

fisco-bcos手动搭建webase启动注意事项

手动搭建webase-front启动注意事项 Java环境变量&#xff1a;1.8.301时候的错误 一直提示节点连接不上&#xff0c;无法连接chanale端口 这是官方提供的解决办法Help wanted: solution for secp256k1 being disabled Issue #470 FISCO-BCOS/java-sdk Java SDK 2.x连接节点失败…...

ospf 的 状态机详解

OSPF&#xff08;开放最短路径优先&#xff0c;Open Shortest Path First&#xff09;协议的状态机是其核心部分之一&#xff0c;用于确保路由器之间的邻接关系&#xff08;neighbor relationship&#xff09;建立和路由信息的交换。OSPF的状态机模型由多个状态组成&#xff0c…...

TP5 动态渲染多个Layui表格并批量打印所有表格

记录&#xff1a; TP5 动态渲染多个Layui表格每个表格设置有2行表头&#xff0c;并且第一行表头在页面完成后动态渲染显示内容每个表格下面显示统计信息可点击字段排序一次打印页面上的所有表格打印页面上多个table时,让每个table单独一页 后端代码示例&#xff1a; /*** Nod…...

spring专题笔记(六):bean的自动装配(自动化注入)-根据名字进行自动装配、根据类型进行自动装配。代码演示,通俗易懂。

目录 一、根据名字进行自动装配--byName 二、根据类型进行自动装配 byType 本文章主要是介绍spring的自动装配机制&#xff0c; 用代码演示spring如何根据名字进行自动装配、如何根据类型进行自动装配。代码演示&#xff0c;通俗易懂。 一、根据名字进行自动装配--byName Us…...

监听器listener

文章目录 监听器( listener)对Application内置对象监听的语法和配置对session内置对象监听的语法和配置 监听器( listener) 对象与对象的关系&#xff1a; 继承关联 tomcat一启动创建的顺序&#xff1a;监听器&#xff0c;config&#xff0c;application(全局初始化参数)&am…...

重温设计模式--10、单例模式

文章目录 单例模式&#xff08;Singleton Pattern&#xff09;概述单例模式的实现方式及代码示例1. 饿汉式单例&#xff08;在程序启动时就创建实例&#xff09;2. 懒汉式单例&#xff08;在第一次使用时才创建实例&#xff09; 单例模式的注意事项应用场景 C代码懒汉模式-经典…...

Flutter动画学习二

如何在 Flutter 中使用自定义动画和剪裁&#xff08;clipping&#xff09;实现一个简单的动画效果。 前置知识点学习 AnimationController AnimationController 是 Flutter 动画框架中的一个核心类&#xff0c;用于控制动画的生命周期和状态。它提供了一种灵活的方式来定义动…...

讯飞语音听写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 类的设计与实现&#xff1a;一个基于 C 的 TCP 客户端框架 在现代网络编程中&#xff0c;TCP&#xff08;传输控制协议&#xff09;客户端是实现网络通信的基础组件之一。本文将详细介绍一个基于 C 的 TcpClient 类的设计与实现&#xff0c;该类提供了创建 TCP 连接…...

PG备份恢复--pg_dump

pg_dump pg_dump 是一个逻辑备份工具。使用 pg_dump 可以在数据库处于使用状态下进行一致 性的备份,它不会阻塞其他用户对数据库的访问 。 一致性备份是 pg_dump 开始运行时&#xff0c;给数据库打了一个快照&#xff0c;且在 pg_dump 运行过程 中发生的更新将不会被备份。 …...

pikachu靶场搭建详细步骤

一、靶场下载 点我去下载 二、靶场安装 需要的环境&#xff1a; mysqlApaches&#xff08;直接使用小皮面板Phpstudy&#xff1a;https://www.xp.cn/&#xff09;&#xff0c;启动他们 设置网站&#xff0c;把靶场的路径对应过来 对应数据库的信息 由于没有核对数据库的信…...

HarmonyOS NEXT开发进阶(五):装饰器讲解

一、Provide Consume 父组件与子组件的子组件(官方叫法&#xff1a;后代组件)双向同步数据&#xff08;即&#xff0c;父组件与后代组件可以相互操作 Provide 修饰的数据&#xff09; 注意&#xff1a;Provide 与 Consume声明的变量名必须一致。 import {TestChild } from .…...

【编译原理】往年题汇总(山东大学软件学院用)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;编译原理_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...

【漏洞复现】F5 BIG-IP Next Central Manager SQL注入漏洞(CVE-2024-26026)

免责声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。工具来自网络,安全性自测,如有侵权请联系删除。本次测试仅供学习使用,如若非法他用,与平台和本文作…...

OpenClaw+Qwen3-14B镜像实战:5分钟搭建飞书智能助手

OpenClawQwen3-14B镜像实战&#xff1a;5分钟搭建飞书智能助手 1. 为什么选择这个组合&#xff1f; 上周三晚上11点&#xff0c;我正在为第二天的部门会议整理材料时&#xff0c;突然冒出一个想法&#xff1a;能不能让AI自动处理这些重复性工作&#xff1f;经过一番折腾&…...

陈文自媒体:暗水印功能上线,2类玩家要发财了!

作者陈文&#xff0c;公众号&#xff1a;陈文日记&#xff0c;90后草根创业者&#xff0c;5年自媒体经验&#xff0c;聚焦体育自媒体和小红书商单&#xff0c;关注我&#xff0c;越分享收获越多。 2026年4月了&#xff0c;抖音最牛逼的暗水印上线了&#xff0c;很多千川的老铁麻…...

AI Agent学习日记 Day3

今天没怎么搞&#xff0c;只做了一点小优化。之前我是用 agent.stream(invoke_input,stream_mode["messages", "updates"],config {"configurable": {"thread_id": "1"}}) 通过mode "messages"来获取并流式输…...

Go语言的HTTP服务器:从基础到高级

Go语言的HTTP服务器&#xff1a;从基础到高级 HTTP 服务器的重要性 在现代 Web 开发中&#xff0c;HTTP 服务器是构建 Web 应用程序的核心组件。一个高性能、可靠的 HTTP 服务器可以&#xff1a; 处理客户端请求&#xff0c;返回响应支持各种 HTTP 方法和状态码提供路由和中…...

数据管理效率低下?MongoDB Compass 重新定义数据库可视化:从入门到精通的非线性学习路径

数据管理效率低下&#xff1f;MongoDB Compass 重新定义数据库可视化&#xff1a;从入门到精通的非线性学习路径 【免费下载链接】compass The GUI for MongoDB. 项目地址: https://gitcode.com/gh_mirrors/com/compass 当你面对命令行中密密麻麻的 MongoDB 数据时&…...

大以论文与万方、维普、WPS AI 综合对比(2026)

毕业季论文格式问题频发&#xff0c;手动排版耗时、通用模板不匹配、公式图表易错乱是常态。万方、维普以查重为主&#xff0c;WPS AI 偏向通用办公&#xff0c;而大以论文作为7 年专注毕业论文排版的老牌工具&#xff0c;在专业性、稳定性与院校适配性上更具优势。一、核心对比…...

Oracle数据库进程体系结构概述

Oracle数据库进程体系结构概述 Oracle属于多进程体系架构&#xff0c;它由多个后台进程组成&#xff0c;每个后台进程完成特定的维护任务&#xff0c;进程之间互相协助&#xff0c;最终共同完成数据库所需的维护任务。 本文讲述的内容&#xff1a; 1、进程类型 2、Oracle两种服…...

基于51单片机的电子秤(4挡)proteus、原理图、流程图 1185-基于51单片机的电子秤...

基于51单片机的电子秤&#xff08;4挡&#xff09;proteus、原理图、流程图 1185-基于51单片机的电子秤&#xff08;4挡&#xff09;proteus、原理图、流程图、物料清单、仿真图、源代码 功能介绍&#xff1a; 1、基本部分 &#xff08;1&#xff09;称重范围用开关分为三挡&am…...

如何一步一步地获取和风天气的天气数据(2026版)

如何一步一步地获取和风天气的天气数据&#xff08;2026版&#xff09;一、和风天气核心优势二、前期准备2.1 注册和风天气开发者账号2.2 创建项目并获取认证密钥&#xff08;API 项目ID/JWT Token&#xff09;2.2.1 登录控制台 → 进入项目管理 → 点击创建项目。2.2.2 填写项…...

小鹅通重磅升级|AI Agent 能力全面护航,私域智能经营新范式

*文中配图及虚拟数据仅作效果展示 随着人工智能越来越贴近经营场景&#xff0c;小鹅通紧扣各位商家的实际需求&#xff0c;在原有产品基础上&#xff0c;以AI Agent为核心&#xff0c;完成了新一轮产品升级——在原有功能基础上&#xff0c;引入场景skills&#xff0c;并通过sk…...