当前位置: 首页 > 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)

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

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

【java面试】微服务篇

【java面试】微服务篇 一、总体框架二、Springcloud&#xff08;一&#xff09;Springcloud五大组件&#xff08;二&#xff09;服务注册和发现1、Eureka2、Nacos &#xff08;三&#xff09;负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...

spring boot使用HttpServletResponse实现sse后端流式输出消息

1.以前只是看过SSE的相关文章&#xff0c;没有具体实践&#xff0c;这次接入AI大模型使用到了流式输出&#xff0c;涉及到给前端流式返回&#xff0c;所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...

npm安装electron下载太慢,导致报错

npm安装electron下载太慢&#xff0c;导致报错 背景 想学习electron框架做个桌面应用&#xff0c;卡在了安装依赖&#xff08;无语了&#xff09;。。。一开始以为node版本或者npm版本太低问题&#xff0c;调整版本后还是报错。偶尔执行install命令后&#xff0c;可以开始下载…...