There never was a good knife made of bad steel.
— Benjamin Franklin
photo by Ales
Krivec(https://unsplash.com/@aleskrivec?utm_source=templater_proxy&utm_medium=referral)
on Unsplash
总览
| 1 |
Meet Safe and Unsafe |
unsafe 语义、UB 清单、健全性与模块化 |
| 2 |
Data Layout |
repr 家族、对齐与大小、DST/ZST/空类型 |
| 3 |
Ownership |
引用、别名、生命周期、variance、dropck、PhantomData |
| 4 |
Type Conversions |
强制转换、点操作符、as、transmute |
| 5 |
Uninitialized Memory |
初始化分析、drop 标志、MaybeUninit |
| 6 |
OBRM (RAII) |
构造/析构约定、递归 drop、泄漏与 mem::forget |
| 7 |
Unwinding |
栈展开、两级异常安全、中毒 |
| 8 |
Concurrency |
数据竞争、Send/Sync、原子与内存顺序 |
| 9 |
Implementing Vec |
稳定 Rust 从零手写 Vec |
| 10 |
Implementing Arc |
手写 Arc:引用计数与内存顺序 |
| 11 |
FFI |
extern、导出符号、repr(C)、C-unwind |
| 12 |
Beneath std |
no_std 与 panic_handler |
章节
第 1 章 Meet Safe
and Unsafe(认识安全与不安全)
1.0 Safe 与 Unsafe 的二元结构
Rust = Safe Rust(真正的 Rust,绝不产生 UB)+
Unsafe Rust(额外允许几件危险的事)。
| let v = vec![1, 2, 3]; let s: &str = "hello";
|
1.1 Safe 与 Unsafe 如何交互
unsafe 关键字的两种用途:①
声明编译器无法检查的契约存在;② 声明程序员已确认契约被遵守。
| unsafe fn get(idx: usize) -> i32; unsafe trait SafeToUse;
unsafe { v.get_unchecked(0) } unsafe impl Send for MyType {}
|
健全性 (soundness)——Safe Rust
的唯一根本性质:无论怎样,Safe Rust 都不可能导致
UB。由此产生不对称的信任关系:
graph TD
A["Safe 代码"] -->|"无条件信任"| B["Unsafe 代码"]
B -->|"不能信任"| C["泛型 Safe 代码<br/>用户传入的 Ord 或闭包"]
B -->|"可以信任"| D["具体实现:整数、切片<br/>风险可衡量"]
C -. 泛型信任的解法 .-> E["unsafe trait<br/>责任转移给实现者"]
| impl Ord for Sloppy { fn cmp(&self, _) -> Ordering { Ordering::Less } }
|
1.2 Unsafe 能做什么(UB 清单)
Unsafe 额外能做的只有五件事:解引用裸指针、调用
unsafe 函数、实现 unsafe trait、访问可变 static、访问 union 字段。
| let p: *const i32 = std::ptr::null(); unsafe { *p }; unsafe { std::mem::transmute::<u8, bool>(3) }; unsafe { *(0x7fffffffffffffff as *const u8) };
loop {} deadlock(); a + b
|
记忆口诀:"安全"≠"正确"——Rust 只承诺不
UB,不承诺程序逻辑正确喵。
1.3 与 Unsafe
共事:健全性是非局部的
健全性依赖非局部状态——纯 Safe 代码的一处改动就能摧毁
unsafe 的契约:
| fn index(idx: usize, arr: &[u8]) -> Option<u8> { if idx < arr.len() { unsafe { Some(*arr.get_unchecked(idx)) } } else { None } }
impl<T> Vec<T> { fn make_room(&mut self) { self.cap += 1; } }
|
结论:unsafe
污染的最小可靠边界是模块——用私有性把能触碰不变式的代码关在模块内:
| mod my_vec { pub struct Vec<T> { ptr: NonNull<T>, cap: usize, len: usize } }
|
第 2 章 Data
Layout(数据布局)
2.1 repr(Rust):默认布局
- 对齐 (alignment):值为
n
的类型只能放在 n 的倍数地址;总是 2 的幂
- 关键不变量:大小必须是对齐的倍数(保证数组可按
size 步进索引)
| struct A { a: u8, b: u32, c: u16 }
|
默认布局是刻意不指定的——只保证同一类型布局一致,不同类型无任何保证:
| struct A { x: u8, y: u32 } struct B { x: u8, y: u32 }
Foo<u16, u32> Foo<u32, u16>
size_of::<Option<&T>>() == size_of::<&T>() size_of::<Option<Box<T>>>() == size_of::<Box<T>>()
|
2.2 特殊大小类型
DST(动态大小类型):编译期大小未知,只能活在指针之后;指向
DST 的指针是宽指针:
| &dyn Trait == { data_ptr, vtable_ptr } &[T] == { data_ptr, len } &str == { data_ptr, len }
struct MySuperSlice { info: u32, data: [u8] } struct Bad { data: [u8], info: u32 }
|
ZST(零大小类型):所有操作都是
no-op,泛型语境威力巨大:
| struct Nothing; let a = Nothing; let b = Nothing; size_of::<Nothing>() == 0
type Set<K> = std::collections::HashMap<K, ()>;
|
空类型:永远无法实例化,用于类型层面表达"不可能":
| enum Void {} let Ok(x) = res::<T, Void>;
let p: *const Void = dangling_ptr(); unsafe { *p };
|
2.3 其他 repr
| #[repr(C)] struct PointC { x: f32, y: f32 }
#[repr(transparent)] struct Meters(f32)
#[repr(u8)] enum Color { Red = 0, Green, Blue }
#[repr(C, packed)] struct Wire { a: u8, b: u32 }
#[repr(align(64))] struct CacheLine([u8; 64])
|
repr(u8) 抑制 niche |
#[repr(u8)] enum MyOption<&u16> 占 16
字节,不带则 8 字节(空指针优化被关掉) |
| 非法枚举值 = UB |
unsafe { transmute::<i32, Color>(99) } ❌ 即使带
repr(C) 也不允许无对应变体的值 |
| packed 取引用 |
let r = &w.b; ❌ 未对齐引用,未来是硬错误 |
| Option 与 FFI |
Option<&T> /
Option<extern fn> 与 T 布局相同,可对应
C 的可空指针 |
第 3 章 Ownership(所有权)
3.1 引用:全部模型只有两条规则
| fn dangle() -> &String { &String::new() }
let mut x = 0; let r1 = &mut x; let r2 = &mut x; *r1 += 1;
|
3.2 别名 (Aliasing):优化的基石
指向重叠内存的指针互为别名。&mut
的无别名保证让编译器敢做激进优化:
| fn compute(input: &u32, output: &mut u32) { if *input > 10 { *output = 1; } if *input > 5 { *output *= 2; } }
fn compute_opt(input: &u32, output: &mut u32) { let v = *input; if v > 10 { *output = 2; } else if v > 5 { *output = 2; } }
|
3.3 生命周期与 NLL
生命周期 =
引用必须有效的区域;借用活到最后一次使用(NLL),不是作用域结束:
| let mut data = vec![1, 2, 3]; let x = &data[0]; println!("{}", x); data.push(4);
|
flowchart LR
A["x 借用 data"] -->|"借用开始"| B["最后一次使用 x"]
B -->|"借用结束"| C["x 不再活跃"]
C -->|"data 恢复独占"| D["data.push(4) 合法"]
D --> E["作用域结束<br/>与借用无关"]
陷阱:Drop 被视为最后一次使用:
| struct X<'a>(&'a i32); let x = X(&data[0]); println!("{}", x.0); data.push(4); drop(x); data.push(4);
|
3.4
生命周期的局限(系统比语义粗)
| struct Foo; impl Foo { fn mutate_and_share(&mut self) -> &Self { &*self } fn share(&self) {} } let mut foo = Foo; let loan = foo.mutate_and_share(); foo.share(); println!("{:?}", loan);
|
3.5 生命周期省略规则
| fn f(x: &str, y: &str)
fn substr(s: &str, until: usize) -> &str
fn f(&mut self, args: &[T]) -> &mut Command
fn get_str() -> &str fn frob(s: &str, t: &str) -> &str
|
3.6 无界生命周期
规则:凡不派生于输入的输出生命周期都是无界的——比
'static 还强:
| fn get_str<'a>(s: *const String) -> &'a str { unsafe { &*s } }
fn get_str_safe(s: &String) -> &str { unsafe { &*(s as *const String) } }
|
3.7 高阶 trait 约束 (HRTB)
| fn foo<F>(f: F) where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8
|
3.8 子类型与可变性 (Variance)
'long <: 'short(活得久的是子类型,反直觉但正确):
| let s: &'static str = "hi"; fn takes_short<'a>(s: &'a str) {} takes_short(s);
|
必背 variance 表:
&'a T |
协变 |
协变 |
|
&'a mut T |
协变 |
不变 |
|
Box<T> / Vec<T> |
|
协变 |
|
UnsafeCell<T> / Cell<T> |
|
不变 |
|
fn(T) -> U |
|
逆变 |
协变 |
*const T |
|
协变 |
|
*mut T |
|
不变 |
|
为什么 &mut T 必须不变——否则经典
UAF:
| fn assign<T>(input: &mut T, val: T) { *input = val; } let mut hello: &'static str = "hello"; { let world = String::from("world"); assign(&mut hello, &world); } println!("{}", hello);
|
| fn store(f: fn(&'static str)) {} fn any_life<'a>(s: &'a str) {} store(any_life);
|
冲突法则:参数用于多个字段时,全协变才协变、全逆变才逆变、否则不变(不变赢得一切冲突)。
3.9 Drop 检查 (dropck)
Big Rule:泛型类型的泛型参数必须严格比它活得久 (strictly
outlive):
| struct Inspector<'a>(&'a u8); impl Drop for Inspector<'_> { fn drop(&mut self) { println!("{}", self.0); } } struct World<'a> { inspector: Option<Inspector<'a>>, days: Box<u8> }
|
逃生舱
#[may_dangle](承诺析构不碰过期数据,编译器不检查):
| unsafe impl<#[may_dangle] 'a> Drop for Inspector<'a> { fn drop(&mut self) { } }
|
3.10 PhantomData
问题:生命周期与结构体逻辑相关却不出现在字段里(无界参数在 struct
定义中被禁止):
| struct Iter<'a, T: 'a> { ptr: *const T, end: *const T, _marker: PhantomData<&'a T>, }
|
使用模式速查:
PhantomData<T> |
协变 / 继承 / 不允许(声明"拥有 T") |
PhantomData<&'a T> |
协变 / 需 T: Sync / 允许 |
PhantomData<&'a mut T> |
不变 / 继承 / 允许 |
PhantomData<*const T> |
协变 / !Send+!Sync / 允许 |
PhantomData<*mut T> |
不变 / !Send+!Sync / 允许 |
PhantomData<fn(T)> |
逆变 / Send+Sync / 允许 |
PhantomData<fn() -> T> |
协变 / Send+Sync / 允许 |
退出 Unpin 用专用类型 PhantomPinned;已有
Drop impl 的类型再加 PhantomData<T> 对
dropck 是多余的,但 variance/auto trait 效果仍在。
3.11 拆分借用
借用检查器懂结构体字段、不懂数组元素:
| let mut x = (1, 2); let a = &mut x.0; let b = &mut x.1; let mut v = vec![1, 2]; let a = &mut v[0]; let b = &mut v[1];
pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { let len = self.len(); let ptr = self.as_mut_ptr(); unsafe { assert!(mid <= len); (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) } }
|
惊喜结论:IterMut 可用纯 Safe
代码实现——关键是"每个元素至多被产出一次":
| fn next(&mut self) -> Option<&'a mut T> { self.cur.take().map(|node| { self.cur = node.next.as_mut(); &mut node.elem }) }
|
第 4 章 Type
Conversions(类型转换)
转换光谱(安全 → 危险):解构重建 → 强制转换 → 点操作符 →
as → transmute。
4.1 强制转换
| let x: &i32 = &mut 0; let v: Vec<i32> = vec![]; let s: &[i32] = &v;
trait Trait {} impl Trait for &i32 {} fn foo<T: Trait>(t: T) {} foo(&mut 0);
|
4.2 点操作符
方法查找算法(伪代码):
| value.foo() 解析顺序: 1. T::foo(value) 按值调用 2. <&T>::foo(&value) autoref 共享 <&mut T>::foo(&mut value) autoref 可变 3. 对 Deref 目标 U 重复 1-2 解引用(Box→T, Rc→T...) 4. unsize: [T; 3] → [T] 忘掉编译期长度 5. 全部失败 → 编译错误
|
autoref 陷阱:derive(Clone) 附带
T: Clone 约束,泛型下 clone 克隆出引用:
| #[derive(Clone)] struct Container<T>(Arc<T>);
fn f<T>(bar: &Container<T>) { let c = bar.clone(); }
impl<T> Clone for Container<T> { fn clone(&self) -> Self { Self(Arc::clone(&self.0)) } }
|
4.3 as 转换
性质:运行时从不失败(截断/饱和静默发生);非法组合编译期拒绝;cast
本身不是 unsafe。
| let a = 300i32 as u8; let b = 3.9_f64 as i32; let c = 7u8 as bool; let d = 65u8 as char; let e = 65i32 as char;
let s: *const [u16] = ...; let t = s as *const [u8];
|
4.4 Transmute
mem::transmute<T, U>
按位重解释,唯一检查是尺寸相同——"最可怕的 unsafe 操作":
| unsafe { transmute::<u8, bool>(3) } unsafe { transmute::<&i32, &mut i32>(r) } unsafe { transmute::<Vec<i32>, Vec<u32>>(v) } let r: &str = unsafe { transmute(ptr) };
unsafe { transmute::<[MaybeUninit<T>; N], [T; N]>(arr) } #[repr(transparent)] struct Wrapper(i32); unsafe { transmute::<i32, Wrapper>(x) }
|
第 5 章 Uninitialized
Memory(未初始化内存)
把未初始化内存当作任何类型的值解读 = UB。
5.1 检查式:Safe Rust
的初始化分析
| let x: i32; if cond { x = 1; } else { x = 2; } println!("{}", x);
let y: i32; if cond { y = 1; } println!("{}", y);
let z; loop { if true { z = 0; break; } } println!("{}", z);
let s = String::from("a"); let t = s;
|
5.2 Drop 标志
编译器用运行时 drop flag 跟踪"该不该调析构器":
| let x; if cond { x = Box::new(0); }
let y = Box::new(0); *y2 = Box::new(1);
|
flowchart TD
A["变量离开作用域"] --> B{"初始化状态静态可知?"}
B -->|"直线代码 或 分支一致"| C["静态 drop<br/>零运行时开销"]
B -->|"条件初始化 或 提前 return"| D["运行时 drop flag<br/>判断后才调用析构器"]
5.3 非检查式与 MaybeUninit
三步初始化模式(Safe Rust 无法部分初始化数组):
| let mut arr: [MaybeUninit<Box<u32>>; N] = unsafe { MaybeUninit::uninit().assume_init() };
for i in 0..N { arr[i] = MaybeUninit::new(Box::new(i as u32)); }
let arr: [Box<u32>; N] = unsafe { mem::transmute(arr) };
|
stateDiagram-v2
state "未初始化" as s1
state "部分初始化" as s2
state "完全初始化" as s3
[*] --> s1: uninit 创建
s1 --> s2: 逐元素填充
s2 --> s2: 继续填充
s2 --> s3: 最后一个元素就绪
s3 --> [*]: assume init 或 transmute
note right of s2: panic 时最多泄漏已初始化部分
关键工具与规则:
| ptr::write(ptr, val); let v = ptr::read(ptr); ptr::copy(src, dst, n); ptr::copy_nonoverlapping(src, dst, n);
let mut u = MaybeUninit::<Demo>::uninit(); let r = unsafe { &u.as_mut().field }; let p = unsafe { &raw mut (*u.as_mut_ptr()).field }; unsafe { p.write(true); } let demo = unsafe { u.assume_init() };
|
第 6 章
OBRM(基于所有权的资源管理 / RAII)
获取资源 = 创建对象;释放资源 = 销毁对象。Rust
关注控制权。
6.1 构造器
创建实例的方式有且只有一种——命名类型并一次性初始化全部字段:
| let foo = Foo { a: 0, b: 1 };
impl Foo { pub fn new() -> Self { Foo { a: 0, b: 1 } } pub fn with_capacity(n: usize) -> Self { ... } }
|
6.2 析构器:递归 drop 与
double-free
drop 运行后 Rust 会自动递归 drop
所有字段(不可关闭):
| impl<T> Drop for SuperBox<T> { fn drop(&mut self) { unsafe { dealloc(self.my_box.ptr); } } }
struct SuperBox<T> { my_box: Option<Box<T>> } impl<T> Drop for SuperBox<T> { fn drop(&mut self) { if let Some(b) = self.my_box.take() { drop(b); } } }
|
flowchart TD
A["SuperBox 离开作用域"] --> B["执行 SuperBox 的 drop"]
B --> C{"my_box 字段状态?"}
C -->|"未 take,仍是 Some"| D["递归 drop 执行 Box 的 drop"]
B2["自己又 dealloc 了同一指针"] --> E["double-free 崩溃"]
C -->|"已 take 成 None"| F["递归 drop 无事可做"]
6.3 泄漏:mem::forget
是安全函数
铁律:unsafe
代码绝不能把"析构器一定运行"作为安全前提:
| mem::forget(my_box);
impl<'a, T> Drain<'a, T> { pub fn drain(vec: &'a mut Vec<T>) -> Self { let iter = ...; vec.len = 0; Drain { vec, iter } } }
|
第 7 章 Unwinding(栈展开)
7.1 展开机制
分层错误处理:Option(缺失)→
Result(可恢复)→ panic(不可恢复)→
abort(灾难)。
7.2 两级异常安全
- 最小异常安全(unsafe 必须):panic
不违反内存安全(状态"安全",不必一致)
- 最大异常安全(safe 最好):panic
后程序仍做正确的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| fn push_all(&mut self, other: &[T]) { unsafe { self.set_len(self.len + other.len()); } for (i, x) in other.iter().enumerate() { self.ptr.add(i).write(x.clone()); } }
fn push_all(&mut self, other: &[T]) { for (i, x) in other.iter().enumerate() { if self.len == self.cap { self.grow(); } unsafe { ptr::write(self.ptr.add(self.len), x.clone()); } self.len += 1; } }
struct Hole<'a, T> { elem: &'a mut T } impl<T> Drop for Hole<'_, T> { fn drop(&mut self) { } }
|
7.3 Poison中毒
| let guard = mutex.lock().unwrap(); let guard2 = mutex.lock();
|
第 8 章 Concurrency(并发)
8.1 数据竞争 vs 竞态条件
数据竞争 = UB,三条同时满足:并发访问同一位置 +
至少一个写 + 至少一个非同步。
| let i = idx.load(Ordering::SeqCst); println!("{}", data[i]);
if idx.load(Ordering::SeqCst) < data.len() { unsafe { data.get_unchecked(idx.load(Ordering::SeqCst)) } }
|
Rust
不阻止一般竞态条件(死锁等)——那是"安全"的,只是程序可能不对。
8.2 Send 与 Sync
|
unsafe impl<T: Send> Send for Carton<T> {} unsafe impl<T: Sync> Sync for Carton<T> {}
|
8.3 原子操作与内存顺序
四种 Ordering(Rust 继承 C++20 内存模型):
| x.store(1, Ordering::SeqCst);
while lock.compare_and_swap(false, true, Ordering::Acquire) {}
lock.store(false, Ordering::Release);
counter.fetch_add(1, Ordering::Relaxed);
|
graph LR
subgraph 线程A
A1["写数据 data=42"] --> A2["lock.store<br/>Release"]
end
subgraph 线程B
B2["lock.load<br/>Acquire"] --> B1["读数据<br/>保证看到 42"]
end
A2 -->|"同步边 happens-before"| B2
经验法则:SeqCst 保底;锁用 Acquire/Release;纯计数用
Relaxed。并发算法要在弱序硬件(ARM)上测试——x86
强序上可能"碰巧能跑"。
第 9 章 Implementing Vec(实现
Vec)
用稳定
API(NonNull、std::alloc、ptr
系)从零手写 std::Vec。
9.1 布局
| pub struct Vec<T> { ptr: NonNull<T>, cap: usize, len: usize, } unsafe impl<T: Send> Send for Vec<T> {} unsafe impl<T: Sync> Sync for Vec<T> {}
|
9.2 分配:三个坑
| impl<T> RawVec<T> { fn new() -> Self { let ptr = NonNull::dangling(); Self { ptr, cap: 0 } } }
let layout = Layout::array::<T>(cap).unwrap(); assert!(layout.size() <= isize::MAX as usize);
let ptr = unsafe { alloc(layout) }; if ptr.is_null() { handle_alloc_error(layout); }
|
9.3 Push 与 Pop:ptr::write /
read
核心区别:赋值会"求值旧值 + drop",ptr::write
只盲目覆写:
| pub fn push(&mut self, elem: T) { if self.len == self.cap { self.grow(); } unsafe { ptr::write(self.ptr.as_ptr().add(self.len), elem); } self.len += 1; } pub fn pop(&mut self) -> Option<T> { if self.len == 0 { return None; } self.len -= 1; unsafe { Some(ptr::read(self.ptr.as_ptr().add(self.len))) } }
|
9.4 释放
| impl<T> Drop for Vec<T> { fn drop(&mut self) { while let Some(_) = self.pop() {} if self.cap != 0 { unsafe { dealloc(self.ptr.as_ptr(), Layout::array::<T>(self.cap).unwrap()); } } } }
|
9.5 Deref:免费继承切片全家桶
| impl<T> Deref for Vec<T> { type Target = [T]; fn deref(&self) -> &[T] { unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } } }
|
9.6 Insert 与 Remove
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| pub fn insert(&mut self, index: usize, elem: T) { assert!(index <= self.len); if self.len == self.cap { self.grow(); } unsafe { ptr::copy(self.ptr.as_ptr().add(index), self.ptr.as_ptr().add(index + 1), self.len - index); ptr::write(self.ptr.as_ptr().add(index), elem); } self.len += 1; } pub fn remove(&mut self, index: usize) -> T { assert!(index < self.len); self.len -= 1; unsafe { let elem = ptr::read(self.ptr.as_ptr().add(index)); ptr::copy(self.ptr.as_ptr().add(index + 1), self.ptr.as_ptr().add(index), self.len - index); elem } }
|
9.7 IntoIter:接管分配 + 双指针
| pub struct IntoIter<T> { buf: RawVec<T>, iter: RawValIter<T> }
impl<T> Vec<T> { pub fn into_iter(self) -> IntoIter<T> { let vec = ManuallyDrop::new(self); let iter = RawValIter::new(unsafe { &*vec }); let buf = unsafe { ptr::read(&vec.buf) }; IntoIter { buf, iter } } }
|
双指针迭代器与 next_back 的坑:
| 内存布局:[ X, X, X, O, X, X, X ] ↑ 只剩一个元素时:start 在 O 左,end 在 O 右 → start != end → 可区分"还有 1 个" ❌ 若 end 直接指向待读元素:start == end 与"耗尽"无法区分 ✅ next_back 必须"先退一格,再读": fn next_back(&mut self) -> Option<T> { if self.end == self.start { None } else { self.end = self.end.sub(1); // 先退 unsafe { Some(ptr::read(self.end)) } // 后读 } } // ⚠️ cap == 0 时 end 不许 offset(dangling 不属于任何分配,GEP 会撒谎)
|
9.8 RawVec:抽象重复
| struct RawVec<T> { ptr: NonNull<T>, cap: usize } pub struct Vec<T> { buf: RawVec<T>, len: usize }
let vec = ManuallyDrop::new(self); let iter = RawValIter::new(&vec); let buf = ptr::read(&vec.buf);
|
9.9 Drain 与泄漏放大
| pub struct Drain<'a, T> { vec: *mut Vec<T>, _marker: PhantomData<&'a mut Vec<T>>, }
impl<'a, T> Vec<T> { pub fn drain(&mut self) -> Drain<'a, T> { let iter = RawValIter::new(self); unsafe { self.set_len(0); } Drain { vec: self as *mut _, iter, _marker: PhantomData } } }
|
9.10 零大小类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| impl<T> RawVec<T> { fn new() -> Self { if size_of::<T>() == 0 { Self { ptr: NonNull::dangling(), cap: usize::MAX } } else { } } fn grow(&mut self) { if size_of::<T>() == 0 { panic!("cap overflow"); } } }
struct RawValIter<T> { start: *const T, end: *const T } impl<T> Iterator for RawValIter<T> { fn next(&mut self) -> Option<T> { if size_of::<T>() == 0 { if self.start == self.end { return None; } self.start = (self.start as usize + 1) as *const T; return unsafe { Some(ptr::read(NonNull::<T>::dangling().as_ptr())) }; } } }
|
第 10 章 Implementing
Arc(实现 Arc)
简化版 Arc(无 weak;原书 Mutex 部分尚未发布)。
10.1 布局:计数与数据同块分配
| pub struct Arc<T> { ptr: NonNull<ArcInner<T>>, phantom: PhantomData<ArcInner<T>>, } struct ArcInner<T> { rc: AtomicUsize, data: T, }
unsafe impl<T: Sync + Send> Send for Arc<T> {} unsafe impl<T: Sync + Send> Sync for Arc<T> {}
|
10.2 Clone:Relaxed + 防回绕
| impl<T> Clone for Arc<T> { fn clone(&self) -> Self { let inner = unsafe { self.ptr.as_ref() }; let old = inner.rc.fetch_add(1, Ordering::Relaxed); if old >= isize::MAX as usize { std::process::abort(); } Self { ptr: self.ptr, phantom: PhantomData } } }
|
10.3 Drop:Release 递减 + Acquire
栅栏
| impl<T> Drop for Arc<T> { fn drop(&mut self) { let inner = unsafe { self.ptr.as_ref() }; if inner.rc.fetch_sub(1, Ordering::Release) != 1 { return; } atomic::fence(Ordering::Acquire); unsafe { Box::from_raw(self.ptr.as_ptr()); } } }
|
graph LR
subgraph 线程A
A1["写 data 经 Mutex"] --> A2["fetch_sub<br/>Release"]
end
subgraph 线程B
B2["fetch_sub 返回 1"] --> B3["fence Acquire"] --> B4["删除 data"]
end
A2 -->|"happens-before"| B3
A1 -. 保证可见 .-> B4
为什么需要 Acquire:Arc<Mutex<T>>
场景下,Mutex
析构时不加锁——不能指望锁自己的同步逻辑。必须由 Release 递减 +
Acquire 栅栏保证"他人对数据的写"对"删除数据的人"可见。
第 11 章 FFI(外部函数接口)
11.1 调用外部函数 + 安全封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #[link(name = "snappy")] extern "C" { fn snappy_compress(input: *const u8, in_len: size_t, output: *mut u8, out_len: *mut size_t) -> c_int; }
pub fn compress(src: &[u8]) -> Vec<u8> { unsafe { let max = snappy_max_compressed_length(src.len()); let mut dst = Vec::with_capacity(max as usize); let mut len = 0; snappy_compress(src.as_ptr(), src.len(), dst.as_mut_ptr(), &mut len); dst.set_len(len as usize); dst } }
|
11.2 从 C 调用 Rust、回调与链接
| #[unsafe(no_mangle)] pub extern "C" fn hello_from_rust() { println!("Hello from Rust!"); }
extern "C" fn on_event(data: c_int) { } unsafe { register_callback(Some(on_event)); }
|
11.3 与外部代码互操作
| #[repr(C)] struct Point { x: f32, y: f32 }
let c = CString::new("hello").unwrap(); c_str.as_ptr()
extern "C" { fn set_cb(cb: Option<extern "C" fn(c_int) -> c_int>); }
|
11.4 FFI 与展开
|
extern "C-unwind" fn may_panic() { panic!("..."); }
#[unsafe(no_mangle)] pub extern "C" fn api() { if let Err(_) = catch_unwind(|| { real_api() }) { eprintln!("rust panicked"); } }
|
11.5 不透明结构体
|
#[repr(C)] pub struct Foo { _data: (), _marker: PhantomData<(*mut u8, PhantomPinned)>, }
|
第 12 章 Beneath std(深入
std 之下)
12.1 no_std 可执行文件模板
| #![no_std] #![no_main]
#[unsafe(no_mangle)] extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int { 0 }
#[lang = "eh_personality"] fn rust_eh_personality() {}
#[panic_handler] fn panic(_info: &PanicInfo) -> ! { core::intrinsics::abort() }
|
| ⚠️ 要点: - 依赖 libc 时 default-features = false(libc 默认特性包含 std!) - windows-msvc 目标没有 libc(extern crate libc 反而报错) - 自建 core 的目标可能要手动链接 compiler_builtins(解决 __aeabi_memcpy 未定义)
|
12.2 panic crate 模式
|
#[panic_handler] fn panic(_: &PanicInfo) -> ! { loop {} }
#[panic_handler] fn panic(info: &PanicInfo) -> ! { }
#[cfg(debug_assertions)] extern crate panic_semihosting; #[cfg(not(debug_assertions))] extern crate panic_halt;
|
核心思想
1. 一条根本保证
健全性:无论怎样,Safe Rust
都不可能导致未定义行为。
unsafe
的意义不是"逃离类型系统",而是在类型系统之下构建新的安全抽象,把安全向上托举给
Safe Rust。
2. 一组不对称的信任关系
- Safe Rust 无条件信任接触到的 Unsafe Rust;
- Unsafe Rust 不能信任泛型 Safe 代码(BTreeMap
不信任用户的
Ord);
- 限制 unsafe 影响范围的唯一可靠边界 = 模块 +
私有性。
3. 一份 UB 清单与一套纪律
| 每处 unsafe 都要能回答: ① 我依赖什么契约?(索引在界内?指针有效?已初始化?) ② 契约由谁保证?(上方检查?模块不变式?调用者承诺?) ③ panic 路径上契约还成立吗? ④ 被 mem::forget 后果是什么?(答案必须是"泄漏"而非 UB) 回答不出来 = 不要写。
|
4. 一套底层工具词汇表
ptr::write / ptr::read |
初始化 / 移出(绕过赋值的"读旧值+drop") |
ptr::copy |
memmove(重叠区间安全,参数序与 C 相反) |
mem::forget / ManuallyDrop |
阻止析构、防双重释放 |
Option::take |
从字段中安全移出值(Drop 解法标配) |
MaybeUninit |
未初始化内存的唯一正确处理 |
NonNull + PhantomData |
裸指针容器标配:variance + dropck + auto trait |
#[repr(C)] / #[repr(transparent)] |
唯一可依赖的布局保证 |
Ordering |
SeqCst 保底 / 锁 Acquire-Release / 计数 Relaxed |
NonNull::dangling() |
ZST 与"未分配"的合法占位指针 |
5. 一场实战收束(第 9-10
章串起全书)
| 手写 Vec/Arc 时用到的知识点映射: 布局(§2) → NonNull + dangling + isize::MAX 限制 所有权(§3) → PhantomData 声明借用语义 未初始化(§5) → ptr::write 初始化 / set_len 时机 RAII(§6) → 递归 drop、防双重释放三件套、leak amplification 异常安全(§7) → panic 路径不暴露未初始化内存 并发(§8) → Relaxed clone + Release/Acquire drop、计数回绕 abort 读懂这两个实现,unsafe Rust 的核心纪律就基本掌握了
|
附:术语中英对照速查
| soundness |
健全性 |
| Undefined Behavior (UB) |
未定义行为 |
| aliasing |
别名 |
| variance (covariant/contravariant/invariant) |
可变性(协变/逆变/不变) |
| drop check (dropck) |
Drop 检查 |
| unwinding |
栈展开 |
| exception safety |
异常安全 |
| poisoning |
中毒 |
| data race / race condition |
数据竞争 / 竞态条件 |
| dangling pointer |
悬垂指针 |
| niche / null pointer optimization |
空位 / 空指针优化 |
| DST / ZST |
动态 / 零大小类型 |
| OBRM / RAII |
基于所有权的资源管理 |
| leak amplification |
泄漏放大 |
| GEP (getelementptr) |
LLVM 指针偏移指令 |
| HRTB |
高阶 trait 约束 |
| opaque struct |
不透明结构体 |