Rust 基础知识体系概览
什么是 Rust?
Rust 是 Mozilla 于 2010 年首次公开、2015 年发布 1.0 的系统级编程语言。它的核心目标是同时提供 C/C++ 级别的性能和内存安全保证——不需要垃圾回收器(GC),通过编译期的所有权系统在零运行时开销下消除数据竞争和悬垂引用。
Rust 连续多年在 Stack Overflow 开发者调查中被评为最受喜爱的编程语言,这不是偶然—— 它让开发者在写代码时就能发现绝大多数内存和并发问题,而不是在生产环境中排查 Segfault。
- 无 GC 的内存安全:通过所有权 + 借用检查器在编译期保证内存安全,零运行时开销
- 无数据竞争:
Send/Synctrait 在编译期阻止数据竞争,实现"无畏并发" - 零成本抽象:泛型、迭代器、闭包等高级特性编译后与手写底层代码性能一致
- 丰富的类型系统:代数数据类型(
enum)、模式匹配、Trait 系统比传统 OOP 更灵活 - 没有 null:用
Option<T>替代 null,编译器强制你处理"值可能不存在"的情况
为什么 Rust 越来越重要?
Rust 正在以惊人的速度渗透到各个领域:
| 领域 | 代表项目 |
|---|---|
| 操作系统 | Linux 内核(Rust for Linux)、Redox OS、Windows 驱动 |
| 浏览器引擎 | Servo、Firefox(Stylo CSS 引擎) |
| 云原生 | Firecracker(AWS Lambda 底层)、Bottlerocket OS |
| 数据库 | TiKV、SurrealDB、Databend |
| 前端工具链 | SWC、Rspack、Turbopack、Oxc、Biome、Lightning CSS |
| Web 框架 | Axum、Actix-web、Rocket |
| 区块链 | Solana、Polkadot(Substrate)、Near Protocol |
| 嵌入式 | Embassy、RTIC |
| 桌面应用 | Tauri(Electron 替代) |
| 命令行工具 | ripgrep、fd、bat、exa、delta |
Rust 是后 C++ 时代的系统编程语言——用编译期检查换取运行时安全和性能,特别适合对性能和可靠性有极高要求的场景。
核心知识点
所有权(Ownership)——Rust 的灵魂
所有权是 Rust 最核心的概念,一切内存安全保证都建立在它之上。三条铁律:
- 每个值有且只有一个所有者
- 当所有者离开作用域时,值被自动释放(Drop)
- 赋值或传参会转移所有权(Move),除非类型实现了
Copy
fn main() {
let s1 = String::from("hello"); // s1 拥有这个 String
let s2 = s1; // 所有权转移到 s2,s1 不再可用
// println!("{}", s1); // ❌ 编译错误:value used after move
println!("{}", s2); // ✅ s2 是当前所有者
}
- Move 语义(默认):
String、Vec、Box等堆上数据赋值时转移所有权 - Copy 语义:
i32、f64、bool、char、元组(元素均 Copy)等栈上数据赋值时自动拷贝
判断规则:如果类型实现了 Copy trait,赋值时复制而非移动。实现了 Drop trait 的类型不允许同时实现 Copy。
详细内容请阅读 所有权
借用与引用(Borrowing)——不转移所有权的访问
不想转移所有权,可以通过引用(借用)来访问数据:
| 类型 | 语法 | 规则 |
|---|---|---|
| 共享引用(不可变) | &T | 同时可以有多个共享引用 |
| 可变引用 | &mut T | 同一时间只能有一个可变引用 |
核心约束:共享引用和可变引用不能同时存在——这就是 Rust 在编译期阻止数据竞争的关键。
fn main() {
let mut s = String::from("hello");
let r1 = &s; // ✅ 共享引用
let r2 = &s; // ✅ 多个共享引用可以共存
println!("{}, {}", r1, r2);
// r1, r2 的最后一次使用在这里,之后它们不再活跃(NLL)
let r3 = &mut s; // ✅ 此时没有活跃的共享引用,可以创建可变引用
r3.push_str(" world");
println!("{}", r3);
}
详细内容请阅读 借用与引用
生命周期(Lifetime)——引用的有效期
生命周期确保引用不会比被引用的数据活得更久。大多数情况下编译器能自动推断(生命周期省略规则),但有些情况需要手动标注:
// 'a 标注告诉编译器:返回值的引用至少和 x、y 中较短的那个一样长
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
详细内容请阅读 生命周期
数据类型——丰富的类型系统
Rust 的类型系统兼具表达力和安全性:
| 分类 | 类型 | 说明 |
|---|---|---|
| 标量类型 | i8i128、u8u128、isize/usize、f32/f64、bool、char | 固定大小,栈上分配 |
| 复合类型 | 元组 (T1, T2)、数组 [T; N] | 固定大小的复合 |
| 字符串 | String(堆,可变)、&str(切片引用) | UTF-8 编码 |
| 集合 | Vec<T>、HashMap<K, V>、HashSet<T> | 动态大小,堆上分配 |
| 枚举 | enum(可携带数据的代数类型) | Option<T>、Result<T, E> |
| 结构体 | struct(命名字段/元组/单元结构体) | 自定义数据类型 |
| Smart Pointer | Box<T>、Rc<T>、Arc<T>、RefCell<T> | 智能指针 |
详细内容请阅读 数据类型、String 与 &str、结构体与枚举
模式匹配——穷尽检查的分支控制
Rust 的 match 是穷尽的——你必须处理所有可能的情况,编译器会帮你检查:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(String), // 枚举变体可以携带数据
}
fn value_in_cents(coin: &Coin) -> u32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("Quarter from {}", state);
25
}
}
}
除了 match,还有 if let(只关心一种情况)和 while let(循环匹配)等简写形式。
详细内容请阅读 模式匹配
错误处理——没有异常,只有 Result
Rust 没有传统的 try/catch 异常机制,而是用类型系统处理错误:
| 类型 | 用途 | 变体 |
|---|---|---|
Option<T> | 值可能不存在 | Some(T) / None |
Result<T, E> | 操作可能失败 | Ok(T) / Err(E) |
? 操作符让错误传播变得简洁:
use std::fs;
use std::io;
fn read_username() -> Result<String, io::Error> {
// ? 遇到 Err 自动 return Err,Ok 则取出值
let content = fs::read_to_string("username.txt")?;
Ok(content.trim().to_string())
}
详细内容请阅读 错误处理
Trait——Rust 的接口与多态
Trait 定义了一组行为(方法签名),类似其他语言的接口,但更强大:
trait Summary {
// 必须实现的方法
fn summarize(&self) -> String;
// 有默认实现的方法
fn preview(&self) -> String {
format!("{}...", &self.summarize()[..20])
}
}
struct Article {
title: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}: {}", self.title, self.content)
}
}
Trait 在 Rust 中的地位极其重要——泛型约束、运算符重载、标记类型(Send/Sync/Copy)等都通过 Trait 实现。
详细内容请阅读 Trait 基础
闭包——捕获环境的匿名函数
Rust 闭包通过三种 Trait 来区分捕获方式:
| Trait | 捕获方式 | 说明 |
|---|---|---|
Fn | &self(不可变借用) | 可多次调用,不修改环境 |
FnMut | &mut self(可变借用) | 可多次调用,会修改环境 |
FnOnce | self(获取所有权) | 只能调用一次,消耗捕获的值 |
fn main() {
let mut numbers = vec![1, 2, 3];
// FnMut 闭包:可变借用 numbers
let mut push_value = |v| numbers.push(v);
push_value(4);
push_value(5);
println!("{:?}", numbers); // [1, 2, 3, 4, 5]
}
详细内容请阅读 闭包
迭代器——零成本的函数式风格
Rust 迭代器是惰性的,只有在消费时才会执行。编译器会将迭代器链优化为等价的手写循环(零成本抽象):
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 链式调用:过滤偶数 → 平方 → 求和
let sum: i32 = numbers.iter()
.filter(|&&x| x % 2 == 0) // 保留偶数
.map(|&x| x * x) // 平方
.sum(); // 求和
println!("偶数平方和: {}", sum); // 2² + 4² + 6² + 8² + 10² = 220
}
详细内容请阅读 迭代器
模块与 Crate——代码组织
Rust 的代码组织层次:
crate(一个编译单元)
├── mod(模块,用 mod 关键字声明)
│ ├── 子模块
│ └── 子模块
├── mod
│ └── ...
└── main.rs / lib.rs(crate 根)
- Crate:最小编译单元,分为 binary crate(可执行)和 library crate(库)
- Module:命名空间,控制可见性,用
pub暴露项 - Cargo.toml:包管理配置,一个 Package 可以包含多个 Crate
详细内容请阅读 模块与 Crate
知识图谱
学习路径建议
- 第一阶段:所有权 → 借用 → 生命周期(这三个是 Rust 的核心,必须彻底理解)
- 第二阶段:数据类型 → 结构体与枚举 → 模式匹配 → 错误处理
- 第三阶段:Trait → 闭包 → 迭代器
- 第四阶段:模块与 Crate → 实际项目练习
Rust 的学习曲线确实陡峭,主要体现在**和编译器"斗争"**上。但换个角度看——编译器报的每个错误,都是在帮你避免一个潜在的运行时 bug。不要害怕编译错误,它们是 Rust 最好的老师。
推荐姿势:先理解为什么 Rust 要这样设计(每个规则背后都有安全理由),再记怎么写。