高级特性:unsafe、trait 进阶与宏 — 第 20 章拆解
高级特性:unsafe、trait 进阶与宏
这一章讲什么: 前五章之外的"偶尔用到"之物,五格抽屉。本章自称参考章: 不必通记,知道每件东西存在、解决什么问题,用的时候回得来查。 五格:unsafe Rust(绕过编译器检查的手动负责区)、trait 进阶、类型进阶、 函数与闭包进阶、宏(写"生成代码的代码")。
1. Unsafe Rust:不是关闭检查,是接过职责
动机:静态分析天生保守——编译器宁可拒绝正确的程序,不放行错误的。另外,底层硬件本身不安全:要直接和操作系统打交道、甚至写操作系统,就得有不安全的操作。unsafe Rust 是你的"我来兜底"声明:编译器,我读过文档,我保证手动守住那些规矩1。
unsafe 块里的五种超能力(别处都不行):① 解引用裸指针;② 调用 unsafe 函数或方法;③ 访问或修改可变静态变量;④ 实现 unsafe trait;⑤ 访问 union 的字段2。
裸指针(*const T / *mut T,星号属于类型名)与引用不同:可无视借用规则(同一位置同时有可变和不可变指针)、不保证指向有效内存、允许空、无自动清理。创建裸指针可以在 safe 代码里做,解引用必须 unsafe(&raw const x 是 raw borrow 操作符)。注意书里演示的:同一 num 上同时建 *const 和 *mut 能通过可变指针改数据——潜在 data race,"Be careful!"3。
调 unsafe 函数:函数定义前加 unsafe,调用时必须包在 unsafe 块里——块的存在等于"我核实过满足了契约"(E0133)。连 unsafe 函数体内做 unsafe 操作也仍需 unsafe 块——让 unsafe 块尽量小,便于审查4。典型模式:用安全函数包住 unsafe 代码(标准库 split_at_mut 的例子)——unsafe 集中一处,对外仍是安全 API5。
extern 与 FFI:edition 2024 里 unsafe extern "C" { fn abs(input: i32) -> i32; } 声明外部函数(ABI 约定)——extern 块本身必须标 unsafe,因为别的语言不保证 Rust 的规则,编译器查不了,契约责任在程序员;块内每一项都隐式 unsafe,像 abs 这样无内存安全顾虑的可以用 safe fn 显式标回安全,标了之后调用不再需要 unsafe 块(但「标了 safe 不等于真安全」,只是你向 Rust 立的保证)6。反向地,#[unsafe(no_mangle)] pub extern "C" fn call_from_c() 让 Rust 函数被 C 调用——no_mangle 在 edition 2024 里也是 unsafe 属性(跨库可能撞名,导出名安全由你负责)7。可变静态量:static mut 全局可变,读写都在 unsafe 里(多线程即 data race)。unsafe trait:trait 声明带 unsafe,实现也带 unsafe——表示"这个 trait 有编译器查不了的约定"(如 Send/Sync 的手动实现,第 16 章)8。
Miri:unsafe 代码的动态检查器(nightly:cargo +nightly miri run)。借用检查器是编译期静态工具,只能拒绝可疑代码;Miri 是运行时的,能发现"编译器放行了但其实有未定义行为"的执行路径。诚实声明:Miri 抓到的必是 bug,没抓到不等于没问题(动态分析只覆盖跑到的路径)9。
2. 高级 trait
关联类型(associated type):trait 声明里的类型占位,实现者填具体类型。Iterator 的 type Item 就是。和泛型参数的关键区别:泛型参数允许一个类型多次实现同一 trait(Iterator<String> for Counter 和 Iterator<u32> for Counter 可以并存,用时得标注选哪个);关联类型每个类型只能实现一次,不用标注。少了灵活性,换来"实现唯一、签名干净"10。
默认类型参数:<Placeholder = ConcreteType>——实现者能用默认值就啥都不用写。招牌用例:运算符重载——Rust 不许造新运算符,但实现 std::ops 里的 trait 就能重载已有运算符:impl Add for Point { type Output = Point; ... } 让两个 Point 能相加(Add 的 RHS 类型参数默认 Self)11。
完全限定语法:<Type as Trait>::function(receiver_if_method, args)——当一个值上多个 trait/自身都有同名方法时消歧。supertrait:trait OutlinePrint: fmt::Display 表示"实现我的类型必须先实现 Display"。newtype:struct Wrapper(Vec<String>)——绕开孤儿规则拿 trait 实现权的标准手法(本地新类型上想配什么 trait 配什么 trait),也是"类型即单位"的轻量表达(Meters/Millimeters)12。
3. 高级类型
newtype(见上)、类型别名(type Kilometers = i32;——同一个类型换个可读的名字,零开销,不构造新类型)。
never 类型 !:空类型,没有值。函数返回 ! 表示永不返回(发散函数,如 panic!/loop)。用途在类型系统的角落:如 match 的某臂发散时,整支仍可参与类型推导(continue 的"值"就是 !,可以配任何类型)13。
动态大小类型(DST):str(不是 &str!)是 DST——长度运行期才知道,所以同一类型的所有值必须等大这条铁规被违反,不能直接声明 str 变量或收 str 参数。解法你已经会了:引用/Box——&str 只存(起始位置, 长度),指针本身大小固定。trait 对象 dyn Trait 也是 DST。配套概念 Sized trait:编译期大小已知的类型自动实现;泛型函数默认 T: Sized,要接受 DST 得写 ?Sized14。
4. 函数与闭包进阶
函数指针 fn(小写):类型而非 trait,fn(i32) -> i32 可直接当参数类型;函数名自动强转成它。fn 实现全部三个 Fn trait,所以收闭包的地方总能传函数,反过来 fn 参数不接收捕获了环境的闭包(C FFI 场景只好用 fn)。返回值想给闭包:用 impl Fn(i32) -> i32(第 10 章的 impl Trait);要返回多种不同闭包,用 trait 对象 Box<dyn Fn...>15。
5. 宏
宏是"写其他代码的代码",编译期展开。两类:
- 声明宏(declarative macro):
macro_rules!定义,"按示例匹配替换"。vec![1,2,3]的真身就是一个 match 结构:匹配( $( $x:expr ),* )模式,展开成递归的 push 调用。比函数强在:参数个数不固定、能生成新类型/函数、在编译期按"代码的形状"分支16。 - 过程宏(procedural macro):更像函数——吃 TokenStream 吐 TokenStream,三种:自定义 derive(
#[derive(...)],如第 5 章的 Debug)、属性宏、函数式宏。实现细节本书不讲,指向 The Rust Programming Language 之外的资料17。
6. 作者的判断与证据
- "unsafe 是接过编译器的职责,不是关掉它"——贯穿全章的立场14。
- "关联类型 = 每类型一次实现,换免标注"——设计取舍的明确对比10。
- "Miri 抓到必有 bug,没抓到不等于干净"——诚实的能力边界9。
判断(我们的,不是书里的): 本章五格里,unsafe 一节的价值密度最高: 它完整回答了"安全边界在哪"——编译器查不了的五件事,由 unsafe 块圈出来、 由程序员签字、由 Miri 抽查。这个"圈出来"的动作本身就是安全工程: 全代码库 grep
unsafe块,审查面是封闭的。对比没有 unsafe 概念的语言 (所有代码都是潜在 unsafe 区),Rust 把"危险"从弥漫态变成了可枚举态。 如果错,会错在: "可枚举"依赖团队自律——unsafe 块里调用的 safe 函数 若内部再层层包 unsafe,审查面会暗中扩大;书中 split_at_mut 的"安全包 unsafe"模式 是对的,但前提是包它的 API 真的封住了所有误用路径,这只能靠人验证,编译器不管。
7. 边界与局限
- 宏一章(尤其过程宏)只给地图不给路;想写自己的 derive 要读专门资料。
- Miri 的 Strict Provenance 等细节只露了一面。
- FFI 的正向教程(构建脚本、链接)超出范围。
8. 可带走的
- unsafe 五超能力;创建裸指针安全、解引用 unsafe;unsafe 块越小越好。
- 安全函数包 unsafe 代码 = 标准抽象模式。
- Miri(nightly)动态查 unsafe;抓到必 bug,没抓到不保干净。
- 关联类型 vs 泛型参数:后者可多次实现、要标注。
- 默认类型参数:运算符重载(Add)的形态。
- newtype 绕孤儿规则;类型别名只是别名。
!永不返回;str/dyn Trait 是 DST,只能 behind 指针;?Sized放行。fn是指针类型;返回闭包用 impl Fn,多种闭包用 Box<dyn Fn>。- 声明宏 macro_rules! 按模式展开;过程宏吃 TokenStream。
9. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| unsafe 的存在理由 | Chapter 20: Advanced Features | text/27-ch20-chapter-20-advanced-features.txt:29(搜「static analysis is conservative」) |
| 裸指针 | 同上 | text/27-ch20-chapter-20-advanced-features.txt:80(搜「Dereferencing a Raw Pointer」) |
| unsafe 函数与 E0133 | 同上 | text/27-ch20-chapter-20-advanced-features.txt:158(搜「Calling an Unsafe Function」) |
| extern / FFI | 同上 | text/27-ch20-chapter-20-advanced-features.txt:324(搜「Using extern」) |
| unsafe trait | 同上 | text/27-ch20-chapter-20-advanced-features.txt:477(搜「Implementing an Unsafe Trait」) |
| Miri | 同上 | text/27-ch20-chapter-20-advanced-features.txt:514(搜「Using Miri」) · text/27-ch20-chapter-20-advanced-features.txt:582(搜「doesn’t catch everything」) |
| 关联类型 | 同上 | text/27-ch20-chapter-20-advanced-features.txt:627(搜「pub trait Iterator」) · text/27-ch20-chapter-20-advanced-features.txt:965(搜「multiple implementations」) |
| 默认参数与 Add | 同上 | text/27-ch20-chapter-20-advanced-features.txt:679(搜「Default Generic Parameters」) |
| never 类型 | 同上 | text/27-ch20-chapter-20-advanced-features.txt:1244(搜「Never Type」) |
| DST 与 Sized | 同上 | text/27-ch20-chapter-20-advanced-features.txt:1322(搜「Dynamically Sized Types」) |
| 函数指针与返回闭包 | 同上 | text/27-ch20-chapter-20-advanced-features.txt:1396(搜「Function Pointers」) · text/27-ch20-chapter-20-advanced-features.txt:1488(搜「Returning Closures」) |
| 宏 | 同上 | text/27-ch20-chapter-20-advanced-features.txt:1619(搜「Declarative Macros」) · text/27-ch20-chapter-20-advanced-features.txt:1632(搜「macro_rules」) |
Footnotes
-
出处:「Chapter 20: Advanced Features」第 29 段(
text/27-ch20-chapter-20-advanced-features.txt:29,搜「static analysis is conservative」)。 ↩ ↩2 -
出处:「Chapter 20: Advanced Features」第 49 段(
text/27-ch20-chapter-20-advanced-features.txt:49,搜「unsafe superpowers」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 80 段(
text/27-ch20-chapter-20-advanced-features.txt:80,搜「Dereferencing a Raw Pointer」)与第 145 段(text/27-ch20-chapter-20-advanced-features.txt:145,搜「does no harm」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 158 段(
text/27-ch20-chapter-20-advanced-features.txt:158,搜「Calling an Unsafe Function」)。 ↩ ↩2 -
出处:「Chapter 20: Advanced Features」第 194 段(
text/27-ch20-chapter-20-advanced-features.txt:194,搜「Safe Abstraction」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 337–365 段(
text/27-ch20-chapter-20-advanced-features.txt:337,搜「unsafe extern」;:365,搜「safe fn abs」)。原文:extern 块须标 unsafe;块内每一项隐式 unsafe;abs 无内存安全问题,可用 safe fn 显式标回安全,此后调用不再需要 unsafe 块;「Marking a function as safe does not inherently make it safe」。 ↩ -
出处:「Chapter 20: Advanced Features」第 383–402 段(
text/27-ch20-chapter-20-advanced-features.txt:383,搜「no_mangle」;:397,搜「call_from_c」)。原文:#[unsafe(no_mangle)] pub extern "C" fn call_from_c();no_mangle 告诉编译器不要改函数名,否则别的语言按原名找不到;mangling 去掉后跨库可能撞名,所以是 unsafe 属性,导出名的安全性由程序员负责。 ↩ -
出处:「Chapter 20: Advanced Features」第 404 段(
text/27-ch20-chapter-20-advanced-features.txt:404,搜「Mutable Static Variable」)与第 477 段(text/27-ch20-chapter-20-advanced-features.txt:477,搜「Implementing an Unsafe Trait」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 514 段(
text/27-ch20-chapter-20-advanced-features.txt:514,搜「Using Miri」)与第 582 段(text/27-ch20-chapter-20-advanced-features.txt:582,搜「doesn’t catch everything」)。 ↩ ↩2 -
出处:「Chapter 20: Advanced Features」第 627 段(
text/27-ch20-chapter-20-advanced-features.txt:627,搜「pub trait Iterator」)与第 965 段(text/27-ch20-chapter-20-advanced-features.txt:965,搜「multiple implementations」)。 ↩ ↩2 -
出处:「Chapter 20: Advanced Features」第 679 段(
text/27-ch20-chapter-20-advanced-features.txt:679,搜「Default Generic Parameters」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 969 段(
text/27-ch20-chapter-20-advanced-features.txt:969,搜「Supertraits」)与第 1061 段(text/27-ch20-chapter-20-advanced-features.txt:1061,搜「newtype pattern」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 1244 段(
text/27-ch20-chapter-20-advanced-features.txt:1244,搜「Never Type」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 1322 段(
text/27-ch20-chapter-20-advanced-features.txt:1322,搜「Dynamically Sized Types」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 1396 段(
text/27-ch20-chapter-20-advanced-features.txt:1396,搜「Function Pointers」)与第 1488 段(text/27-ch20-chapter-20-advanced-features.txt:1488,搜「Returning Closures」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 1619 段(
text/27-ch20-chapter-20-advanced-features.txt:1619,搜「Declarative Macros」)与第 1632 段(text/27-ch20-chapter-20-advanced-features.txt:1632,搜「macro_rules」)。 ↩ -
出处:「Chapter 20: Advanced Features」第 1585 段(
text/27-ch20-chapter-20-advanced-features.txt:1585,搜「procedural macros」)。 ↩