尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

玩转Rust高级应用 怎么理解在标准库中,有一个std::intrinsics模块,它里面包含了一系列的编译器内置函数

玩转Rust高级应用 怎么理解在标准库中,有一个std::intrinsics模块,它里面包含了一系列的编译器内置函数
📅 发布时间:2026/7/8 2:38:33

内置函数

在标准库中,有一个std::intrinsics模块,它里面包含了一系列的编译器内置函 数。这些函数都有一个extern"rust-intrinsic"修饰,它们看起来都像一种特殊的 FFI 外部函数,大家打开标准库的源代码src/core/intrinsics.rs,可以看到这些函数 根本没有函数体,因为它们的实现是在编译器内部,而不是在标准库内部。

调用它们的时候 都必须使用unsafe 才可以。编译器见到这些函数,就知道应该生成什么样的代码,而不是像 普通函数调用一样处理。另外,intrinsics 是藏在一个feature gate后面的,这个feature 可能 永远不会稳定,这些函数就不是准备直接提供给用户使用的。一般标准库会在这些函数基础 上做一个更合适的封装给用户使用。

下面就在这些函数中挑一部分作介绍。

transmute

fn transmute<T,U>(e:T)->U函数可以执行强制类型转换。把一个T 类型参 数转换为U类型返回值,转换过程中,这个参数的内部二进制表示不变。但是有一个约束条 件,即size_of::()==size_of::() 。 如果不符合这个条件,会发生编译错误。 transmute_copy的作用跟它类似,区别是,参数类型是一个借用为&T。

一般情况下,我们也可以用as 做类型转换,把&T 类型指针转换为裸指针,然后再转换为&U类型的指针。这样也可以实现类似的功能。但是用户自己实现的泛型函数有一 个缺陷,即无法在where 条件中自己表达size_of::()==size_of::() 。而 transmute作为一个内置函数就可以实现这样的约束。

transmute和 transmute_copy在 std::mem 块中重新导出。用户如果需要,请使用这个模块,而不是std::intrinsics模块。下面用一个示例演示一下Vec 类型的二进制表示是怎样的:

fnmain(){letx=vec![1,2,3,4,5];unsafe{lett:(usize,usize,usize)=std::mem::transmute_copy(&x);println!("{}{}{}",t.0,t.1,t.2);}}

上面的例子中,我们调用了transmute_copy, 因此参数类型是&Vec 。假如我们用 transmute 函数,参数类型就必须是Vec, 区别在于,参数会被move 进入这个函数中, 在后面就不能继续使用了。在调用transmute_copy 函数的时候,必须显示指定返回值类型,因为它是泛型函数,返回值类型可以有多种多样的无穷变化,只要满足size_of:: ()==size_of::() 条件,都可以完成类型转换。

所以编译器自己是无法自动推理出 返回值类型的。在上例中,我们的返回值类型是包含三个usize 的 tuple类型。这是因为, Vec 中实际包含了3个成员,一个是指向堆上的指针,一个是指向内存空间的总大小,还有 一个是实际使用了的元素个数,因此这个类型转换从编译器看来是满足“占用内存空间相同” 这一条件的。

编译执行,我们就可以看到Vec 内部的具体内存表示了。执行结果为:

639392055

intrinsics模块里面有几个与内存读写相关的函数,比如copy 、copy_nonoverla-pping 、write_bytes 、move_val_init 、volatile_load 等。这些函数又在std::ptr/std::mem 模块中做了个简单封装,然后暴露出来给用户使用。下面挑其中几个重要 的函数介绍。

1.copy

copy 的完整签名是unsafe fn copy(src:*const T,dst:mut T,count:usize)。 它做的就是把src 指向的内容复制到dst 中去。它跟C 语言里面的 memmove类似,都假设src 和 dst 指向的内容可能有重叠。区别是memmove 的参数是 void类型,第三个参数是字节长度,而ptr::copy 的指针是带类型的,第三个参数是对象的个数。

这个模块中还提供了ptr::copy_nonoverlapping 。它 跟C 语言里面的memcpy 很像,都假设用户已经保证了src 和 dst 指向的内容不可重叠。所以ptr::copy_nonoverlapping的执行速度比 ptr::copy要快一些。

2.write

在ptr 模块中,write 的签名是unsafe fn write(dst:*mut T,src:T), 作用是把变量src 写入到dst 所指向的内存中。注意它的参数src 是使用的类型T, 执行 的 是move 语义。查看源码可知,它是基于intrinsics::move_val_init 实现的。注 意,在写的过程中,不管dst 指向的内容是什么,都会被直接覆盖掉。而src这个对象也 不会执行析构函数。

写内存还有ptr::write_bytes 、ptr::write_unaligned 、ptr::write_vol- atile 等函数。

3.read

在 ptr 模 块 中 ,read 的 签 名 是unsafe fn read(src:*const T)->T, 作用是把src 指向的内容当成类型T 返回去。查看它的内部源码,可见它就是基于 ptr::copy_nonoverlapping 实现的。

读内存还有ptr::read_unaligned 以 及ptr::read_volatile 两个函数,大同 小异。以上这些内存读写函数,都是不管语义,直接操作内存中的字节。所以它们都是用 unsafe 函数。

4.swap

在 ptr 模块中,swap 的签名是unsafe fn swap(x:*mut T,y:*mut T),作 用是把两个指针指向的内容做交换。两个指针所指向的对象都只是被修改,而不会被析构。

这个函数在mem 模块中又做了一次封装,变成了unsafe fn swap(x:&mut T, y:&mut T)供用户使用。签名中的&mut 型引用可以保证这两个指针是当前唯一指向该对 象的指针。

某些特殊类型还有自己的swap 成员函数。比如Cell::swap(&self,other:&Cell)。 这个函数跟其他swap 函数最大的区别在于,它的参数只要求共享引用,不要 求可变引用。这是因为Cell 本身的特殊性。它具备内部可变性,所以这么设计是完全安全 的。我们可以从源码看到,它就是简单地调用了ptr::swap。

5.drop_in_place

在 ptr 模块中,drop_in_place 的签名是unsafe fn drop_in_place<T:?Sized> (to_drop:*mut T)。它的作用是执行当前指向对象的析构函数,如果没有就不执行。

6.uninitialized

在 mem 模块中,uninitialized 的签名是unsafe fn uninitialized()->T。 它是基于intrinsics::uninit 函数实现的。我们知道,Rust 编译器要求每个变量必须在 初始化之后再使用,如果在某些情况下,你确实需要未初始化的变量,那么必须使用unsafe 才能做到。注意,任何时候读取未初始化变量都是未定义行为,请大家不要这么做。即便你 在unsafe代码中创造了未初始化变量,也需要自己在逻辑上保证,读取这个变量之前先为它 合理地赋过值。

另外,这个函数有点像std::mem::forget, 调用这个函数,不仅不会在程序中增加 代码,反而会减少可执行代码。调用forget, 会导致编译器不再插入析构函数调用的代码, 调用uninitialized会导致缺少初始化。它们没有任何运行开销。

uninitialized函数也还没有稳定,它有一些无法克服的缺陷,将来标准库会废弃掉 这个函数,而使用一个新的类型让用户在unsafe代码中创建未初始化变量。

综 合 示 例

下面我们用一个示例来演示一下这些unsafe函数的用途,以及怎样才能正确调用unsafe 代码。示例很简单,就是实现标准库中的内存交换函数std::mem::swap。

我们可以确定这个函数的签名是fn swap(x:&mut T,y:&mut T)。关于泛 型的解释在第21章中有,此处略过不提。先试一个最简单的实现:

fn swap<T>(x:&mutT,y:&mutT){letz:T=*x;*x=*y;*y=Z;}

编译不通过。因为let z =*x;执行的是move 语义,编译器不允许我们把x 指向的 内容move 出来,这只是一个借用而已。如果允许执行这样的操作,会导致原来的借用指针 x 指向非法数据。但是我们知道,我们这个函数整体上是可以保证安全的,因为我们把x 指 向的内容move 出来之后,会用其他的正确数据填回去,最终可以保证函数执行完之后, x 和y 都是一个正常的状态。这种时候,我们就需要动用unsafe了,代码如下:

fn swap<T>(x:&mutT,y:&mutT){unsafe{letmut t:T=mem::uninitialized();ptr::copy_nonoverlapping(&*x,&mut t,1);ptr::copy_nonoverlapping(&*y,x,1);ptr::copy_nonoverlapping(&t,y,1);mem::forget();}

代码逻辑的意思如下。

首先,我们依然需要 一个作为中转的局部变量。这个局部变量该怎么初始化呢?其实我 们不希望它执行初始化,因为我们只需要这部分内存空间而已,它里面的内容马上就会被覆 盖掉,做初始化是浪费性能。况且,我们也不知道用什么通用的办法初始化 一个泛型类型, 它 连Default约束都未必满足。所以我们要用mem::uninitialized函 数 。

相关新闻

  • 复杂网络节点挖掘 3 类方法对比:中心性、影响力与社区结构,5 大指标实战解析
  • 鲁棒管模型预测控制实战手册:MATLAB实现与扰动处理深度解析
  • 【2026年下】中小学教师资格考试《综合素质》(中学)历年真题和答案

最新新闻

  • GitHub Desktop中文汉化终极指南:3分钟实现完整界面本地化
  • ITSM产品怎么选?四大核心方案解析,国产化与AI原生重构运维价值锚点(2026)
  • 企业级AI Agent架构长啥样?一张图看懂!
  • Alexa Fluor系列蛋白荧光标记一站式定制
  • Transmission Remote GUI终极指南:跨平台远程BT下载管理神器
  • 【Linux基础IO】printf 为什么会写进文件?一文搞懂重定向和 dup2 底层原理

日新闻

  • PROPKA 3深度解析:蛋白质pKa预测的实战指南与算法原理
  • 微信小程序 globalData 监听:基于 Object.defineProperty 的 3 种实现方案对比
  • MySQL 8.0 数据清洗实战:3类异常值识别与 UPDATE/DELETE 批量处理

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号