Featured image of post Rust 全景教程:所有权、生命周期、工程化与 22 个设计模式

Rust 全景教程:所有权、生命周期、工程化与 22 个设计模式

从 cargo 与 workspace 工程化、edition 2024 / resolver = 3 新特性,到所有权、借用、生命周期、模式匹配等核心范式,再到第三方库生态、设计模式与 7 大开发方向,一次性串起 Rust 学习的完整路线图。

为什么写这篇:Rust 的"无 GC 又内存安全"听起来矛盾,能调和它的就是 所有权 + 借用检查器。这套机制不靠运行时、不靠程序员自律,而是 编译期就把空指针、悬垂引用、数据竞争挡在门外。本文以一份"学习路径图"为目标,把语法、工程化、生态、设计模式与开发方向全部串起来——比单点教程更适合作为长期参考。

适用读者:有 C/C++/Java/Go 任意一门语言基础,想系统理解 Rust 范式与生态的开发者。

前置知识:理解指针、堆栈、并发基础,了解命令行工具使用。

目录

  1. 教程资源
  2. 命名规范
  3. cargo 与项目工程化
  4. 入门:关键字、宏、变量与数据类型
  5. 语句、表达式与函数
  6. 所有权:Rust 的核心范式
  7. 借用、切片与生命周期
  8. 复合类型与模式匹配
  9. 方法、泛型、特征与集合
  10. 第三方库速查(按领域)
  11. 22 个设计模式(Rust 实现)
  12. 7 大开发方向
  13. 常见坑与小结

1. 教程资源

类型资源说明
在线教程Rust 语言圣经中文系统教程,覆盖基础到进阶
在线教程Rust Course(书籍版)圣经的精装本
配套练习Rust By Practice边学边练的题目集
练习答案basic-types/functions 答案练习题参考答案
练习站Practice 中文站在线 judge 风格练习
进阶练习Rusty Course进阶专题练习
示例集Rust By Example一段代码一个概念
命名规范Rust 命名指南官方推荐的命名约定
关键字表关键字清单关键字与原生标识符
派生宏derive 速查#[derive(...)] 全部内置 trait
模块系统Item 引用官方模块系统权威定义

Why 多资源:没有哪一份教程能"一站式"——圣经适合系统学习,By Example 适合查阅单点,By Practice 适合练手感。三者配合用最稳。


2. 命名规范

Rust 走的是 蛇形命名法(snake case)为主,关键字小驼峰 + 蛇形穿插。下面是速查表:

命名类型惯例示例
常量全部大写,下划线分隔;数字字面量可加下划线增强可读性const MAX_POINTS: u32 = 100_000;
函数蛇形命名fn greet_world() {}
方法蛇形命名fn add_two() {}
变量蛇形命名let user_name = "..."
类型 / 结构体 / 枚举大驼峰struct UserProfile {}
特征大驼峰trait Summary {}
蛇形 + 感叹号println!, vec!
crate 名蛇形serde_json

原生标识符:如果你想用某个保留字做变量名,前面加 r# 即可,例如 let r#fn = 1;。这叫"原生标识符",仅在少数和宏/外部 API 互操作时才会用到。


3. cargo 与项目工程化

3.1 cargo 是 Rust 的"一切入口"

cargo 同时管 构建、依赖、测试、文档、发布 五个维度。rustc(编译器)能直接调,但实际项目 100% 走 cargo。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
cargo new my_app --vcs none   # 新建项目(不初始化 git)
cd my_app
tree                          # 仅含 Cargo.toml + src/main.rs
cargo run                     # 编译 + 运行(debug)
cargo build --release         # release 模式
cargo check                   # 快速类型检查(不生成二进制,比 build 快 5-10x)
cargo test                    # 跑测试
cargo fmt                     # 格式化
cargo clippy                  # lint
cargo fix                     # 自动修复 clippy warning
cargo add serde               # 加依赖

3.2 中小项目的模块组织

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
project/
├── Cargo.toml
├── src/
│   ├── main.rs                # 二进制入口
│   ├── lib.rs                 # 库入口:pub mod modules;
│   ├── modules.rs             # 声明 mod user;(因为有同名文件夹)
│   └── modules/
│       ├── user.rs            # user 模块
│       └── user/              # user 的子模块目录
│           ├── register.rs
│           └── login.rs
└── tests/
    └── integration_test.rs    # 集成测试(视为外部用户视角)

lib.rspub use 明确暴露模块接口、隐藏内部实现细节。

模块路径 ↔ 文件路径的对应关系:

模块路径文件路径文件内容
cratelib.rsmod modules;
crate::modulesmodules.rsmod user;
crate::modules::usermodules/user.rsmod register;
crate::modules::user::registermodules/user/register.rs(子模块文件)

mod.rs 的弃用:rustc 1.30 之前要靠 mod.rs 加载嵌套子模块,现在统一改成"同名文件夹 + 同名 .rs"约定,避免一个项目出现多个 mod.rs 的混乱

3.3 多 crate Workspace

当项目要拆 library + binary + 多个微服务时,用 workspace:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
my_workspace/
├── Cargo.toml                 # 工作区根配置
├── README.md
├── target/                    # 共享编译目录
├── Cargo.lock                 # 共享锁文件
├── app/                       # 主程序包
│   ├── Cargo.toml
│   └── src/main.rs
└── crates/                    # 子模块包
    ├── core/                  # 核心库
    │   ├── Cargo.toml
    │   └── src/lib.rs
    └── utils/                 # 工具库
        ├── Cargo.toml
        └── src/lib.rs

Cargo.toml(2024 Edition 推荐写法)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[workspace]
# 依赖解析器版本(2024 Edition 默认 resolver = "3",优化了多版本依赖解析)
resolver = "3"

# 声明 workspace 成员(支持通配符)
members = [
    "crates/my_lib",
    "crates/my_bin",
]

# 共享依赖(推荐):统一管理版本,成员 crate 直接引用
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

成员 crate Cargo.toml

1
2
3
4
5
6
7
8
# crates/my_lib/Cargo.toml
[package]
name = "my_lib"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { workspace = true }

workspace 一键操作

1
2
3
4
5
cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo fmt --all
cargo publish -p my-crate-*

3.4 微服务架构示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
rust_iot_platform/
├── Cargo.toml                 # workspace 根
├── api/                       # 接口定义(Proto / OpenAPI / AsyncAPI)
│   ├── proto/                 # gRPC 原始定义
│   └── schema/                # 共享 JSON Schema
├── services/                  # 微服务应用层(binary crates)
│   ├── auth-service/
│   ├── gateway/
│   └── device-manager/
├── components/                # 内部共享组件库(library crates)
│   ├── domain/                # 核心领域模型(纯业务,无 DB、无网络)
│   ├── infra-db/              # 数据库访问层(PostgreSQL + Diesel/SQLx)
│   ├── infra-mq/              # 消息队列适配器(NATS / RabbitMQ)
│   └── common/                # 基础工具(日志、错误处理、配置加载)
├── scripts/                   # CI/CD 脚本、数据库迁移
└── tests/                     # 跨服务 E2E

为什么用 workspace 而不是一个 mega crate:编译速度(增量编译只编译改动的成员)、依赖隔离(domain 不会被 SQLx 拖进依赖图)、发布灵活(每个组件可单独 publish)。

3.5 edition 与 resolver 的关系

Edition引入版本默认 resolver关键变化
2015Rust 1.0 (2015-05)resolver = "1"经典版
2018Rust 1.31 (2018-12)resolver = "2"NLL 借用检查、路径清晰化
2021Rust 1.56 (2021-10)resolver = "2"闭包捕获、Or 模式 prelude
2024Rust 1.85 (2025-02-13)resolver = "3"默认开启 resolver = "3";trait 推导更智能;gen 块;unsafe extern 块显式化

resolver = “3” 的核心收益:避免一些原本需要手动加 Cargo.lock 才能解析的 feature 冲突场景;对 monorepo 友好;与 2024 edition 的 unsafe 显式化配合更安全。


4. 入门:关键字、宏、变量与数据类型

4.1 关键字速查

行为关键字说明
方法fn函数定义
变量let默认不可变
可变mut在引用、裸指针、模式绑定中表明可变性
循环for ... in ...唯一内置的遍历循环
编译期配置debug_assertionsdebug 构建为 true,release 为 false
占位符{:?} / {:.2}调试格式 / 小数位 2
优美输出{:#?}结构化美化打印
类型别名type给复杂类型起短名
方法实现impl给类型 / 特征实现方法
泛型约束where复杂 trait 约束的另一种写法

4.2 常用宏

行为备注
控制台打印println!标准输出,有缓冲
错误输出eprintln!stderr,无缓冲
编译期求值cfg!在编译期计算条件
未实现占位unimplemented!标记"还没写"
模式匹配matches!简洁的 match 等价物
列表vec!vec![1, 2, 3]
调试dbg!输出到 stderr,含文件名 + 行号 + 表达式 + 值
断言assert! / assert_eq!测试 / 不变量检查

4.3 派生宏(derive)

最常用的是 #[derive(Debug)],它自动为结构体 / 枚举生成 {:?} 格式化实现:

1
2
#[derive(Debug)]
struct Point { x: i32, y: i32 }

其他常见的:CloneCopyPartialEqEqHashDefaultPartialOrdOrd,需要在 Cargo.tomlderive feature(部分 trait 由标准库派生宏提供)。

4.4 变量与解构

Rust 的变量 默认不可变——既要灵活性又要安全,所以"可变"必须显式声明:

1
2
3
let x = 5;        // 不可变
let mut y = 5;    // 可变
y += 1;

什么时候选哪个?

决策适用场景
不可变 let默认;写小代码、函数式风格
可变 let mut大数据结构、热点路径(避免克隆带来的再分配)
遮蔽 let x = ...同一名字多次用、做类型转换

未被使用的变量会触发 warning。用下划线开头即可消除,如 let _unused = ...

变量遮蔽(shadowing)mut 的区别:遮蔽是 创建了一个新的、拥有相同名称的变量,可以换类型;mut 是同一变量的可写权限。

1
2
let spaces = "   ";        // &str
let spaces = spaces.len(); // usize,类型都换了

下面的写法 不行(类型不一致):

1
2
let mut spaces = "   ";
spaces = spaces.len();  // 编译错误:&str ≠ usize

解构 的本质是"把整体拆解并绑定到多个具体的名字上":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fn main() {
    struct Point { e: i32 }

    let (a, b, c, d, e);
    (a, b) = (1, 2);
    [c, .., d, _] = [1, 2, 3, 4, 5];
    Point { e, .. } = Point { e: 5 };

    assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
}

4.5 数据类型

类型关键字说明
有符号整数i8 / i16 / i32 / i64 / i128 / isize默认整型 i32
无符号整数u8 / u16 / u32 / u64 / u128 / usizeusize 取决于平台字长
浮点f32 / f64默认 f64,现代 CPU 上两者速度相当
字符串切片&str栈上 16 字节(指针 + 长度),不可变
字符串String栈上 24 字节(指针 + 长度 + 容量),堆上 UTF-8
字符char单个 Unicode 字符(4 字节)
布尔bool1 字节,true / false
单元()唯一值是 (),0 字节
数组[T; N]栈上,长度是类型一部分
切片&[T]运行时长度

isize / usize 在 32 位平台是 4 字节、64 位平台是 8 字节。

数值字面量

1
2
3
4
5
6
let a = 98_222;          // 十进制,下划线增强可读
let b = 0xff;             // 十六进制
let c = 0o77;             // 八进制
let d = 0b1111_0000;      // 二进制
let e = b'A';             // u8 字节字面量
let f = -42.1_f32;        // 类型后缀(防类型推导走偏)

字符 / 布尔 / 单元

1
2
3
let c = 'z';       // 单引号
let t = true;
let u = ();         // 单元类型,0 字节

5. 语句、表达式与函数

Rust 是基于表达式的语言——表达式总要返回值,语句不返回。

1
2
3
4
5
fn add_with_extra(x: i32, y: i32) -> i32 {
    let x = x + 1;  // 语句
    let y = y + 5;  // 语句
    x + y           // 表达式(不能加 `;`)
}

if / match / 块 都是表达式,可以直接赋值:

1
let y = if x % 2 == 1 { "odd" } else { "even" };

表达式不显式 return 时,隐式返回 ()(单元类型)。

函数定义要点

1
fn add(i: i32, j: i32) -> i32 { i + j }
  • 函数名 / 变量名:蛇形命名
  • 函数位置:随便放,编译器不关心
  • 每个参数都必须标注类型

永不返回类型 !(diverging functions)

1
2
3
4
5
6
7
fn dead_end() -> ! {
    panic!("你已经到了穷途末路,崩溃吧!");
}

fn forever() -> ! {
    loop { /* 永不跳出 */ };
}

!bottom type,能匹配任何类型——panic! / loop {} / continue / break 都有 ! 类型的语义。


6. 所有权:Rust 的核心范式

这是 Rust 唯一不可绕过的概念。其他特性都可以边写边学,所有权是编译器的"宪法"。

6.1 堆栈视角:理解所有权的前提

组织方式后进先出(LIFO)散乱无序,依赖指针寻址
大小编译期已知且固定运行时动态申请
速度极快(移动栈指针即可)较慢(要走分配器)
线程关系与线程绑定进程内共享
默认上限Linux/macOS 8MB / Windows 1MB物理内存上限

500KB 局部变量的"生与死"

1
2
3
4
fn process_data() {
    let buffer = [0u8; 500 * 1024];  // 500KB
    println!("首字节: {}", buffer[0]);
}
阶段动作
进入函数CPU 指令 sub rsp, 512000(栈指针下移 500KB)
函数体内栈帧建立,buffer 的"家"在这 500KB
函数结束指令 add rsp, 512000(栈指针上移 500KB),逻辑释放
物理残留那 500KB 物理内存仍含 0,但无法再访问——下个函数会覆盖

架构师视角的注意点

  1. 临界点:500KB + 已用 600KB 超过 Linux 默认 8MB → Stack Overflow
  2. 复制开销:500KB 按值传参会再开 500KB 并物理复制 → 用 &buffer 引用只传 8 字节地址
  3. 缓存抖动:短时间大量栈申请 / 释放会让 L1/L2 频繁失效

6.2 三条铁律

  1. 每个值都被一个变量所拥有
  2. 一个值同时只能被一个变量拥有(所有权唯一)
  3. 当所有者离开作用域,值被 drop

函数返回值也有所有权——return value 本质是把所有权"移"给调用方。

6.3 移动(Move)语义

1
2
3
4
5
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;          // s1 的所有权"移动"到 s2,s1 失效
    // println!("{}", s1); // 编译错误:s1 已经无效
}

为什么不直接让 s1、s2 都指向同一块堆? 因为"双重释放"(两个 drop 调同一块堆)会让程序崩溃。

函数间转移所有权

1
2
3
4
5
6
7
8
fn gives_ownership() -> String {
    let s = String::from("hello");
    s                       // 移出给调用者
}

fn takes_and_gives_back(s: String) -> String {
    s                       // 拿进来再返回
}

6.4 Copy 与 Clone

  • i32 / f64 / bool / char 实现 Copy trait——赋值 复制 而非移动
  • String / Vec<T> 不实现 Copy——赋值 移动
  • 想"复制" String 用 s.clone()(深拷贝)
1
2
3
4
5
let x = 5;
let y = x;                 // 复制(Copy),x 仍可用

let s1 = String::from("hi");
let s2 = s1.clone();       // 深拷贝,s1、s2 都能用

7. 借用、切片与生命周期

7.1 引用:&&mut

引用 = 不夺所有权地使用值:

1
2
3
4
5
6
7
8
9
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // 传引用
    println!("'{}' 长度 {}", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}
引用类型语法规则
不可变引用&T同一作用域可存在 多个
可变引用&mut T同一作用域 只能有一个,且不能与不可变引用共存

违规的混合借用

1
2
3
4
5
let mut s = String::from("hello");
let r1 = &s;       // 不可变
let r2 = &s;       // 不可变
let r3 = &mut s;   // 编译错误:r1/r2 还在作用域
println!("{},{},{}", r1, r2, r3);

合法的非词法作用域版本

1
2
3
4
5
6
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);  // r1/r2 最后一次使用,作用域结束
let r3 = &mut s;                // 此时安全
println!("{}", r3);

7.2 悬垂引用(Dangling References)

Rust 在编译期拒绝悬垂引用:

1
2
3
4
fn dangle() -> &String {  // 编译错误
    let s = String::from("hello");
    &s                     // s 出作用域被 drop
}

修复:返回所有权,不要返回引用。

1
2
3
4
fn no_dangle() -> String {
    let s = String::from("hello");
    s
}

7.3 字符串切片

1
2
3
let s = String::from("hello world");
let hello = &s[0..5];   // "hello"
let world = &s[6..11];  // "world"

左闭右开区间。&str胖指针(指针 + 长度),是 &String 之外更通用的字符串引用形式。

7.4 字符串字面值 vs String

&str(字符串字面值)String
内存编译期硬编码进二进制运行时在堆上分配
可变性不可变可变、可增长
分配静态String::from / String::new
释放进程结束变量离开作用域时 drop

Rust 的"无 GC 又安全"靠的就是变量离开作用域自动 drop——GC 牺牲性能,手动管理牺牲安全,Rust 选了第三条路。

7.5 生命周期标注

大多数时候编译器能自动推导,有歧义时才需手动标

1
2
3
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

'a 的含义:返回的引用的生命周期 ≤ 入参引用的生命周期中较短的那个

生命周期省略规则(编译器自动推的):

  1. 每个引用参数都有独立生命周期参数
  2. 若只有一个引用参数,那个生命周期赋给所有输出
  3. 若有 &self / &mut self,输出生命周期跟 self

结构体中含引用必须 加生命周期——编译器不让你"借用不该借用的数据"。


8. 复合类型与模式匹配

8.1 元组

多种类型组合,长度固定、顺序固定let (x, y) = (1, "hi"); 是常用解构。

8.2 结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#[derive(Debug)]
struct File {
    name: String,
    data: Vec<u8>,
}

fn main() {
    let f1 = File {
        name: String::from("f1.txt"),
        data: Vec::new(),
    };
    let f1_name = &f1.name;
    let f1_length = &f1.data.len();
    println!("{:?}", f1);
}

内存布局File 本身在栈上(nameString 数据 24 字节 + dataVec 数据 24 字节),两个字段的 ptr 指针分别指向各自的堆内存。把字段转移出去后该字段就不可访问,其它字段仍可用

元组结构体(字段没名字):

1
2
3
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);

单元结构体(无字段,只关心行为):

1
2
3
4
struct AlwaysEqual;
let subject = AlwaysEqual;

impl SomeTrait for AlwaysEqual { /* ... */ }

8.3 枚举

枚举允许"列举可能的成员"来定义一个类型,任何类型的数据都可以放入枚举成员中(字符串、数值、结构体、甚至另一个枚举):

1
2
3
enum PokerSuit {
    Clubs, Spades, Diamonds, Hearts,
}

Option<T> 枚举是 Rust 处理"空值"的方式——没有 null,需要表达"可能不存在"时用 Some(T)None

8.4 数组与切片

类型存储长度常用形式
[T; N] 数组编译期固定let a = [1, 2, 3];
Vec<T> 动态数组运行时可变let mut v = vec![1, 2];
&[T] 切片胖指针运行时&a[1..3]&v[..]
  • [u8; 3][u8; 4] 是不同的类型——长度是类型的一部分
  • 实际开发中 数组切片 &[T] 最常用,因为有固定类型大小

8.5 模式匹配

1
2
3
4
5
6
7
8
9
enum Direction { East, West, North, South }

fn main() {
    let dire = Direction::South;
    match dire {
        Direction::East => println!("East"),
        other => println!("other: {:?}", other),
    }
}

要点:

  • match 必须穷举,用 _ 兜底
  • 每个分支都是 表达式,返回值类型必须一致
  • X | Y 表示"或"
  • if let 是单分支版的 match
  • match / if let 的块是新作用域,绑定相当于新变量——同名会发生变量遮蔽,要小心

9. 方法、泛型、特征与集合

9.1 方法(method)

Rust 把"数据"和"行为"分离——结构体定义数据,impl 块定义方法。方法名可以跟字段名相同(常用于 getter)。一个类型可以有多个 impl,按职责拆分:

1
2
3
4
5
6
7
8
impl File {
    pub fn new(name: &str) -> Self { /* ... */ }
    pub fn name(&self) -> &str { &self.name }
}

impl File {
    pub fn read(&self) -> &[u8] { &self.data }
}

好处

  • 不用在函数签名中重复写 self 类型
  • 代码组织性、内聚性更强

9.2 泛型

泛型就是 多态——T 是惯例首选(type 首字母):

1
2
3
4
5
6
7
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in &list[1..] {
        if item > largest { largest = item; }
    }
    largest
}

约束有两种写法:fn foo<T: Trait>(...)where T: Trait。后者更适合复杂约束。

9.3 特征(trait)

特征定义了"一组可以被共享的行为"——只要实现特征,你就能使用这组行为

1
2
3
4
5
6
7
trait Summary {
    fn summarize(&self) -> String;
}

impl Summary for Article {
    fn summarize(&self) -> String { format!("{}: {}", self.title, self.author) }
}

特征是 Rust 实现 接口抽象、零成本抽象、对象安全多态 的核心机制。

9.4 集合(Collection)

集合类型特点
动态数组Vec<T>堆上、可增长、最常用
双端队列VecDeque<T>两端 O(1) push/pop
哈希表HashMap<K, V>O(1) 查找;无序
BTreeMapBTreeMap<K, V>有序、O(log n)
哈希集合HashSet<T>去重、O(1) 查找
BTreeSetBTreeSet<T>有序去重

10. 第三方库速查(按领域)

10.1 核心与基础工具

用途
SerdeRust 序列化和反序列化事实标准,与 JSON / YAML / TOML / BSON 无缝集成
Tokio异步 Rust 的基石,多线程异步运行时
Rayon数据并行化,把计算密集任务快速转多线程
Anyhow灵活的、易用的错误处理(适合应用层)
Thiserror库中定义结构化错误类型

10.2 Web 开发与网络

用途
AxumTokio 团队维护的异步 Web 框架,模块化、类型安全、人体工程学极佳
Actix-web极其快速、成熟的 Web 框架
Rocket易用性极高的 Web 框架,代码简洁、类型安全
Reqwest强大的 HTTP 客户端(同步 + 异步)
TonicHTTP/2 的高性能 gRPC 框架

10.3 数据库与数据持久化

用途
SQLx异步、纯 Rust 的 SQL 驱动,编译期 SQL 语法检查,支持 PG / MySQL / SQLite
Diesel安全、强大的 ORM 框架
Redis-rsRust 官方维护的 Redis 驱动

10.4 命令行工具

用途
Clap解析命令行参数的顶级库,支持子命令、自动帮助
Indicatif优雅的进度条 / 微调器
Colored终端彩色文本输出

10.5 序列化与数据格式

用途
Serde JSONSerde 的 JSON 实现,最广泛
TomlTOML 配置文件
CSV高性能 CSV 处理

10.6 其它优质工具

用途
Tracing结构化、异步的诊断信息追踪与日志
UUIDUUID 生成与处理
Chrono日期 / 时间处理
Lazy Static运行时安全初始化静态变量

选型原则:底层用 Serde + Tokio + SQLx 几乎不会错;Web 框架优先 Axum(生态最热);CLI 必装 Clap + Indicatif;日志统一 Tracing。


11. 22 个设计模式(Rust 实现)

Refactoring Guru 设计模式(Rust 版) 是最权威的 Rust 实现对照表。下面给出 22 个模式 + 一句话定义 + 适用场景

11.1 创建型模式

#模式一句话
1抽象工厂 Abstract Factory创建一系列相关对象,无需指定其具体类
2生成器 Builder分步骤创建复杂对象,相同代码生成不同形式
3工厂方法 Factory Method父类接口决定子类实例化哪个类
4原型 Prototype复制已有对象,不依赖它们所属的类
5单例 Singleton保证类只有一个实例,提供全局访问点

11.2 结构型模式

#模式一句话
6适配器 Adapter让接口不兼容的对象互相合作
7桥接 Bridge把大类拆成"抽象"和"实现"两个独立层次结构
8组合 Composite把对象组合成树状,像用独立对象一样用它们
9装饰 Decorator把对象放入"包装对象"中,绑定新行为
10外观 Facade为复杂库 / 框架提供简单接口
11享元 Flyweight通过共享相同状态,在有限内存中载更多对象
12代理 Proxy为原对象提供替代品,控制访问

11.3 行为模式

#模式一句话
13责任链 Chain of Responsibility把请求沿处理者链发送
14命令 Command把请求转为独立对象,支持参数化、队列化、撤销
15迭代器 Iterator不暴露集合底层表现形式的情况下遍历
16中介者 Mediator限制对象直接交互,迫使通过中介者合作
17备忘录 Memento不暴露实现细节的情况下保存 / 恢复之前状态
18观察者 Observer订阅机制,事件发生时通知多个观察者
19状态 State内部状态变化时改变行为(看起来像换了类)
20策略 Strategy把一系列算法分别封装成类,使它们可互换
21模板方法 Template Method超类定义算法框架,子类重写特定步骤
22访问者 Visitor把算法与其作用的对象隔离开

Rust 实现这些模式的差异:因为所有权和特征的存在,“继承"在 Rust 里是反模式(用组合 + trait 实现)。很多模式在 Rust 里 天然契合(如 Newtype、RAII、Builder、Iterator),但也有 不适合的模式(如抽象工厂在高度泛型代码里会更复杂)。


12. 7 大开发方向

以下不是"应该学哪个"的单选题,而是 7 个真实存在的生态位。Rust 的"无 GC + 内存安全 + 高性能"让它在多个垂直领域都成为首选之一。

方向关键特性契合点代表项目 / 框架
嵌入式开发接近金属、可预测性能、无 GC 暂停、适合 256KB RAM MCURust Embedded
游戏引擎高性能游戏逻辑,ECS 模式天然契合所有权 + 借用Bevy(ECS 优先)、amethyst、macroquad、ggez
区块链 / 密码学内存安全 + 高性能,密码学库与共识实现可避免空指针、悬垂引用solana-program、ink!(智能合约)、parity-common
桌面端Tauri(Web 前端 + Rust 后端,体积小)、egui(immediate mode)Tauri、Slint、egui
Web 后端异步运行时成熟,类型安全Axum、Actix-web、Rocket
前端 WebAssemblyWASM 体积小、性能可预测、与 JS 互操作自然wasm-bindgen、Yew(类 React)、Leptos
命令行工具单文件二进制、跨平台、启动快ripgrep、fd、bat、eza、starship
操作系统内核、驱动、系统级工具redox-os、coreboot 组件、tock

跨端复用套路(Rust + React 系):

类型后端前端
webrustreact
桌面rustreact
移动rustreact native

核心套路:Rust 做底层(性能敏感、调系统 API),前端 / UI 用 React 系。一套技能树覆盖桌面、Web、移动。


13. 常见坑与小结

13.1 编译期就能挡掉的常见坑

表现修法
同时存在可变 + 不可变引用编译错误缩小不可变引用的作用域(让 r1/r2 在 r3 创建前用完)
String 不是 Copy编译错误clone() 或返回所有权
迭代时修改容器编译错误先收完再改、用 iter_mut / 索引遍历
悬垂引用编译错误返回所有权,不要返回引用
生命周期标注歧义编译错误编译器提示加 'a
cargo search 不能用镜像命令无结果必须直连 crates.io
5KB+ 局部数组运行时栈溢出改成 Vec<T>(堆)或缩小

13.2 编辑器与工具链

编辑器推荐
VSCoderust-analyzer 扩展(基于 LSP)
IntelliJ IDEARust 插件(官方收费)
Neovimrust-tools.nvim + rust-analyzer
Helix内置 LSP 客户端,配 rust-analyzer

.vscode/settings.json 推荐配置:

1
2
3
4
5
6
{
  "[rust]": {
    "editor.defaultFormatter": "rust-lang.rust",
    "editor.formatOnSave": true
  }
}

13.3 安装与镜像

1
2
3
4
5
# Linux / macOS
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

# Windows:先装 Microsoft C++ Build Tools(提供 MSVC 链接器)
# 下载 https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe

国内镜像$HOME/.cargo/config.toml):

1
2
3
4
5
[source.crates-io]
replace-with = 'ustc'

[source.ustc]
registry = "https://mirrors.ustc.edu.cn/crates.io-index/"

注意cargo search 不能用镜像,搜索需直连 crates.io。

13.4 一句话总结

Rust 的"无 GC 又内存安全"靠的就是 所有权 + 借用检查器——编译器是警察,你在写代码时就已经在和它博弈。度过这个适应期后,写出来的 Rust 是又安全又快。

5 个最核心概念

  1. 所有权:每个值有唯一所有者,drop 时释放
  2. 借用:用引用不夺所有权,可变借用唯一性
  3. 生命周期:引用的有效范围,编译期检查
  4. 模式匹配match / if let 替代 switch
  5. Cargo workspace:管理多 crate 项目的事实标准

5 个最容易踩的坑

  1. 同时有可变 + 不可变引用 → 缩小不可变引用的作用域
  2. 迭代时修改容器 → 先收完再改、用 iter_mut / 索引遍历
  3. 忘了 clone(),以为 StringCopy → 它不是
  4. 生命周期标注冗余或缺失 → 编译器提示加 'a
  5. cargo search 用不了镜像 → 必须直连 crates.io

数据来源

使用 Hugo 构建
主题 StackJimmy 设计