本页目录

Rust II · 并发、trait 与 unsafe 边界

对标The Rust Programming Language / Rust for Rustaceans(Gjengset)| 前置:rust-01(所有权)、par-02(无锁、数据竞争)、os-02(并发) 这一页把 Rust 的三块高级能力讲透:无畏并发(所有权如何在编译期消灭数据竞争——par 线所有噩梦的解药)、trait(Rust 的抽象与多态机制,接口 + 泛型的统一)、以及 unsafe(什么时候必须跳出安全网、以及安全抽象的边界哲学)。读完,你会明白 Rust 为什么能同时用于操作系统内核和 Web 后端。

1. 无畏并发:数据竞争的编译期终结

par-02/os-02 里,数据竞争是最难调的 bug(间歇性、不可复现)。Rust 的洞见:数据竞争的定义是"≥2 线程访问同一数据、≥1 个写、无同步"——而 rust-01 的借用铁律(共享不可变 XOR 可变不共享)恰好禁止了"可变 + 共享"。把它扩展到线程间,数据竞争就在编译期不可能。靠两个 trait:

编译器自动为大多数类型推导这两个标记,并强制:想跨线程共享可变数据,必须用同步类型——Mutex<T>(互斥锁,os-02)、Arc<T>(原子引用计数,多线程共享所有权)、atomic(无锁原语,par-02)。忘了加锁?编译不过——borrow checker 拦下。"数据竞争在 Rust 里是编译错误而非运行时灾难",这就是"无畏并发"的字面兑现——你可以大胆写多线程,编译过了就没有数据竞争(死锁仍可能,那是逻辑问题 os-02,但数据竞争没了)。

典型模式

这缝合了本站三条线:os-02 的锁/竞态 + par-02 的原子/内存序 + rust-01 的所有权 = Rust 把并发的正确性从"程序员自律"变成"编译器强制"。你读到这里回看 par-02 那些 ABA、内存序噩梦,会明白 Rust 消灭的正是它们。

2. Trait:Rust 的抽象引擎

trait 是 Rust 的接口/多态机制——定义"一个类型能做什么",是泛型和抽象的核心(类似 Java interface + Haskell typeclass,🔗 pl 线的类型类):

读法trait 让 Rust 既有 C 的性能又有高级语言的抽象——泛型 + trait bound 在编译期解决,运行时零负担。你写 .iter().map().filter().collect() 这样的函数式链,编译后和手写循环一样快。

3. unsafe:安全网的边界

Rust 的安全保证靠借用检查器,但有些事它证明不了安全(却确实安全)——底层内存操作、调用 C、实现无锁结构(par-02)、硬件寄存器访问。这时用 unsafe 块,解锁五种超能力(如解引用裸指针、调用 unsafe 函数)。

关键哲学——安全抽象封装 unsafe

这是 Rust 工程哲学的精髓:不是"永远安全"(那做不了系统编程),而是"默认安全 + 显式标记的不安全边界 + 人对边界负责"——大部分代码享受编译器保护,极少数底层代码明确标出、集中审查。这个"把危险局部化并显式化"的思想,本身就是一种优秀的工程纪律(🔗 与 sec 线"缩小攻击面"、系统线"隔离"同构)。

4. Rust 的定位:什么时候用它

5. 练习与要点

例 1(无畏并发实操)Arc<Mutex<T>> 让多个线程安全地共享递增一个计数器——对比 os-02 里 C 版的数据竞争(那里要小心加锁、错了静默出错),Rust 里忘加锁直接编译不过。"编译器逼你正确"亲手体验

例 2(trait 双分发) 同一个 Draw trait,用泛型(静态分发)和 Vec<Box<dyn Draw>>(动态分发)各写一次——理解"编译期特化 vs 运行时虚表"的取舍。

例 3(unsafe 边界) 读标准库 Vec::push 的实现(内部有 unsafe 处理未初始化内存),理解"unsafe 实现 + 安全接口"的封装模式——理解 Rust 不是没有 unsafe,而是把它关进审查过的盒子\(\blacksquare\)


语言线完成(4 页)。下一页进入工程与全栈线——以你正在运营的 Medusa 为解剖标本,看一个请求从浏览器到数据库的完整旅程。