目录
1. 所有权语义深度解析
1.1 Move 语义的原理
Rust 的默认行为是 move(转移所有权),而非 copy。对于未实现 Copy trait 的类型,赋值会转移所有权:
let s = String::from("hello");
let t = s; // s 的所有权 move 到 t
// println!("{}", s); // 编译错误:value borrowed here after move
编译器在 MIR(Middle IR)阶段追踪每个值的所有权状态,确保不会使用已 move 的值。
1.2 Copy trait 的条件
一个类型能实现 Copy 必须满足:
- 所有字段都实现了
Copy - 不包含堆分配(
String、Vec等不行) - 不包含
Droptrait 实现
#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 } // ✅ 纯栈数据
struct Wrapper(String); // ❌ 无法 Copy
1.3 函数参数的所有权传递
fn takes_ownership(s: String) { /* s 的所有权进入函数 */ }
fn borrows(s: &String) { /* 仅借用 */ }
fn mut_borrows(s: &mut String) { s.push('!'); }
fn main() {
let s = String::from("hi");
takes_ownership(s); // s 被 move
// takes_ownership(s); // ❌ 已 move
let mut t = String::from("hello");
borrows(&t); // ✅ 不可变借用
mut_borrows(&mut t); // ✅ 可变借用
}
2. 智能指针全解
2.1 Box:堆分配
let b = Box::new(5); // 5 在堆上分配
let list = Box::new(Node { // 递归结构必须使用 Box
value: 1,
next: Some(Box::new(Node { value: 2, next: None })),
});
Box<T> 提供堆分配且保持单所有权,离开作用域时自动释放堆内存。
2.2 Rc:单线程引用计数
use std::rc::Rc;
let data = Rc::new(String::from("shared"));
let d2 = Rc::clone(&data); // 引用计数 +1
let d3 = Rc::clone(&data); // 引用计数 +1
println!("count: {}", Rc::strong_count(&data)); // 3
// Rc::downgrade 创建弱引用,打破循环引用
⚠️
Rc<T>不是线程安全的(未实现Send+Sync)。
2.3 Arc:原子引用计数(多线程安全)
use std::sync::Arc;
use std::thread;
let data = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for i in 0..3 {
let clone = Arc::clone(&data);
handles.push(thread::spawn(move || {
println!("thread {}: {:?}", i, clone);
}));
}
for h in handles { h.join().unwrap(); }
2.4 RefCell:运行时借用检查
提供「内部可变性」——在不可变引用内部修改数据:
use std::cell::RefCell;
let cell = RefCell::new(5);
*cell.borrow_mut() += 1; // 运行时检查借用规则
println!("{}", cell.borrow()); // 6
// 违反规则会 panic(运行时,非编译期)
// let r1 = cell.borrow_mut();
// let r2 = cell.borrow_mut(); // thread 'main' panicked
2.5 Mutex 与 RwLock
线程安全的内部可变性:
use std::sync::{Arc, Mutex};
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(std::thread::spawn(move || {
let mut num = c.lock().unwrap();
*num += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("count: {}", *counter.lock().unwrap()); // 10
| 智能指针组合 | 单线程 | 多线程 | 内部可变性 |
|---|---|---|---|
Rc<RefCell<T>> | ✅ | ❌ | ✅ |
Arc<Mutex<T>> | ✅ | ✅ | ✅ |
Arc<RwLock<T>> | ✅ | ✅ | ✅(读多写少) |
2.6 Cow:写时克隆
use std::borrow::Cow;
fn title_case(input: &str) -> Cow<'_, str> {
if input.chars().all(|c| !c.is_lowercase()) {
Cow::Borrowed(input) // 无需修改,零拷贝
} else {
Cow::Owned(input.to_uppercase()) // 需要时才分配
}
}
3. 内部可变性模式
内部可变性允许通过不可变引用来修改数据。核心实现有三种:
3.1 Cell(仅适用于 Copy 类型)
use std::cell::Cell;
let cell = Cell::new(5);
cell.set(10); // 不可变引用下修改
println!("{}", cell.get()); // 10
3.2 RefCell(运行时借用检查)
use std::cell::RefCell;
struct Logger {
logs: RefCell<Vec<String>>,
}
impl Logger {
fn log(&self, msg: &str) {
self.logs.borrow_mut().push(msg.to_string()); // &self 下修改
}
}
3.3 OnceCell / LazyLock(延迟初始化)
Rust 1.80 stabilized std::sync::LazyLock:
use std::sync::LazyLock;
static CONFIG: LazyLock<String> = LazyLock::new(|| {
std::fs::read_to_string("config.toml").unwrap()
});
fn main() {
println!("{}", *CONFIG); // 首次调用时初始化
}
4. 自定义 Drop 与 RAII
4.1 自定义 Drop trait
struct FileGuard {
fd: i32,
path: String,
}
impl Drop for FileGuard {
fn drop(&mut self) {
println!("Closing file: {}", self.path);
// unsafe { libc::close(self.fd); }
}
}
fn main() {
let guard = FileGuard { fd: 1, path: "data.txt".to_string() };
// guard 离开作用域时自动调用 drop()
}
4.2 std::mem::forget 与内存泄漏
let s = String::from("leak");
std::mem::forget(s); // 不调用 drop,产生内存泄漏
// 安全但不好;通常应避免
4.3 ManuallyDrop
use std::mem::ManuallyDrop;
let mut m = ManuallyDrop::new(String::from("manual"));
// 不会自动调用 drop
unsafe { ManuallyDrop::drop(&mut m); } // 显式释放
5. 内存布局与对齐
5.1 查看内存布局
println!("size: {}, align: {}",
std::mem::size_of::<String>(),
std::mem::align_of::<String>()
);
5.2 repr 属性控制布局
#[repr(C)] // C 兼容布局
struct CStruct { a: u8, b: u32 }
#[repr(packed)] // 无填充(紧凑但可能慢)
struct Packed { a: u8, b: u32 }
#[repr(align(16))] // 16 字节对齐
struct Aligned { value: u64 }
5.3 零大小类型(ZST)
struct Unit; // size = 0
enum Void {} // size = 0(不可实例化)
let v: Vec<Unit> = vec![Unit; 1000]; // 不分配堆内存!
6. 悬垂指针与数据竞争防御
6.1 编译期防御示例
fn dangling() -> &String {
let s = String::from("dangle");
&s // ❌ 编译错误:返回局部变量的引用
} // s 在这里被 drop
6.2 数据竞争的定义与防御
数据竞争的三个条件(Rust 同时阻止):
- 两个或更多指针访问同一数据
- 至少一个指针写入数据
- 没有同步机制
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // ❌ 编译错误:不能与不可变借用同时存在
// 规则确保条件 2(写入)且条件 3(无同步)的情况被消除
7. Unsafe Rust 安全边界
Unsafe Rust 允许五项操作:
- 解引用裸指针(
*const T,*mut T) - 调用 unsafe 函数
- 实现 unsafe trait
- 访问/修改可变静态变量
- 使用
union字段
7.1 安全抽象封装
pub struct MyVec<T> {
ptr: *mut T,
len: usize,
cap: usize,
}
impl<T> MyVec<T> {
pub fn get(&self, index: usize) -> Option<&T> {
if index >= self.len { return None; }
// unsafe 块最小化
Some(unsafe { &*self.ptr.add(index) })
}
}
原则:unsafe 块应该尽量小,且被安全的 API 封装。调用者无需关心内部 unsafe。
7.2 裸指针 vs 引用
let mut x = 5;
let r = &mut x as *mut i32; // 从引用创建裸指针
unsafe {
*r = 10; // 解引用裸指针
println!("{}", *r);
}
裸指针所有权由程序员管理,无生命周期检查。
7.3 Unsafe 最佳实践
/// # Safety
/// 调用者必须确保 ptr 指向有效的、已初始化的 T 值
/// 且 ptr 的生命周期至少与返回引用一样长
unsafe fn from_raw<'a, T>(ptr: *const T) -> &'a T {
&*ptr
}
8. 性能调优:避免不必要的克隆
8.1 使用引用代替克隆
// ❌ 低效
fn process(data: Vec<String>) -> Vec<String> { ... }
// ✅ 高效:借用
fn process(data: &[String]) -> Vec<String> { ... }
8.2 Cow<’_, str> 策略模式
use std::borrow::Cow;
fn normalize(input: &str) -> Cow<'_, str> {
if input.starts_with("prefix_") {
Cow::Borrowed(input)
} else {
Cow::Owned(format!("prefix_{}", input))
}
}
8.3 使用 &str 而非 String 接收参数
// ❌ 强制调用者 clone
fn greet(name: String) { println!("Hello, {}", name); }
// ✅ 灵活接受 String 和 &str
fn greet(name: &str) { println!("Hello, {}", name); }
greet("world"); // &str
greet(&String::from("world")); // &String 自动解引用
掌握 Rust 的所有权与内存安全系统后,可以写出无需 GC 但保证无内存泄漏、无数据竞争的高性能代码。这是 Rust 区别于其他系统语言的核心竞争力。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。
「rust」更多文章
Rust 错误处理与测试:thiserror、mockall 与属性测试
Rust 错误处理最佳实践:Result/Option 组合子、thiserror 与 anyhow 选型、panic 边界控制、单元/集成/文档测试、mockall 模拟、property-based testing 与代码覆盖率。
Rust 系统编程与性能优化:零拷贝、内存剖析与编译调优
Rust 系统编程深度实践:操作系统原语、零拷贝 I/O、mmap 内存映射、性能剖析(cargo flamegraph)、编译器优化(LTO/PGO)、Benchmark 与内存分析,以及 no_std 嵌入式场景。
Rust 桌面端与 WASM:Tauri、WebAssembly 与嵌入式开发
Rust 跨平台开发全景:Tauri 替代 Electron 的轻量级架构、WASM 编译(wasm-bindgen/wasm-pack)、WASI 运行时、嵌入式 Rust(no_std/embedded-hal),以及跨平台发布策略。