Rust语言编程
前言
本文面向有编程经验的Rust学习者,使用 Cargo 作为包管理器。
参考链接
[6]claude-sonnet-4-5-20250929-thinking
Rust中的变量
Hello world!
fn main() {
println!("Hello, world!");
}
变量命名
在命名方面,和其它语言没有区别,不过当给变量命名时,需要遵循 Rust 命名规范,也不能使用 Rust关键字。
let a = "hello world"
变量绑定
上面这条语句将 "hello world" 字符串绑定给了变量a,"hello world" 的所有权也就属于a。在Rust中任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人。
let a = "hello world"
所有权转移后,该对象之前的主人就会丧失对该对象的所有权。
fn main() {
// 1. 变量绑定 - "hello world" 的所有权属于 a
let a = String::from("hello world");
println!("a 的值: {}", a);
// 2. 所有权转移 - 将 a 的所有权转移给 b
let b = a;
println!("b 的值: {}", b);
// 3. 此时 a 已经失效,无法再使用
// println!("a 的值: {}", a); // ❌ 编译错误!
// 错误信息: borrow of moved value: `a`
}
变量可变性
Rust 的变量在默认情况下是不可变的。
fn main() {
let a = 10;
println!("a is a number: {}", a);
// a = 20; // ❌ 编译错误!无法修改不可变变量
println!("a is a number: {}", a);
}
我们可以通过 mut 关键字让变量变为可变的。
fn main() {
let mut a = 10; // 可变变量
println!("a is a number: {}", a);
a = 20;
println!("a is a number: {}", a);
}
忽略未使用的变量
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 BUG。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
fn main() {
let _x = 5;
let y = 10; // #[warn(unused_variables)]
}
变量解构
let 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:
fn main() {
let (a, mut b): (bool,bool) = (true, false);
// a = true,不可变; b = false,可变
println!("a = {:?}, b = {:?}", a, b);
b = true;
assert_eq!(a, b);
}
解构式赋值
在 Rust 1.59 版本后,我们可以在赋值语句的左式中使用元组、切片和结构体模式了。
struct Struct {
e: i32
}
fn main() {
let (a, b, c, d, e); // // ✅ 仅声明,不c初始化
(a, b) = (1, 2);
// _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有使用一个变量名而是使用了 _
[c, .., d, _] = [1, 2, 3, 4, 5];
Struct { e, .. } = Struct { e: 5 };
// 1 2 1 4 5
println!("a = {}, b = {}, c = {}, d = {}, e = {}", a, b, c, d, e);
}
变量与常量
变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:常量(constant)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异:
- 常量不允许使用
mut。常量不仅仅默认不可变,而且自始至终不可变,因为常量在编译完成后,已经确定它的值。 - 常量使用
const关键字而不是let关键字来声明,并且值的类型必须标注。
下面是一个常量声明的例子,其常量名为 MAX_POINTS,值设置为 100,000。(Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性):
const MAX_POINTS: u32 = 100_000;
常量可以在任意作用域内声明,包括全局作用域,在声明的作用域内,常量在程序运行的整个过程中都有效。对于需要在多处代码共享一个不可变的值时非常有用,例如游戏中允许玩家赚取的最大点数或光速。
变量遮蔽
Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的:
fn main() {
let x = 5;
// 在main函数的作用域内对之前的x进行遮蔽
let x = x + 1;
{
// 在当前的花括号作用域内,对之前的x进行遮蔽
let x = x * 2;
// x = 12
println!("The value of x in the inner scope is: {}", x);
}
// x = 6
println!("The value of x is: {}", x);
}
变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量)。
基本类型
Rust 每个值都有其确切的数据类型,总的来说可以分为两类:基本类型和复合类型。 基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:
- 数值类型:有符号整数 (
i8,i16,i32,i64,isize)、 无符号整数 (u8,u16,u32,u64,usize) 、浮点数 (f32,f64)、以及有理数、复数 - 字符串:字符串字面量和字符串切片
&str - 布尔类型:
true和false - 字符类型:表示单个 Unicode 字符,存储为 4 个字节
- 单元类型:即
(),其唯一的值也是()
数值类型
整数类型
整数是没有小数部分的数字。之前使用过的 i32 类型,表示有符号的 32 位整数( i 是英文单词 integer 的首字母,与之相反的是 u,代表无符号 unsigned 类型)。下表显示了 Rust 中的内置的整数类型:
| 长度 | 有符号类型 | 无符号类型 | 范围 |
|---|---|---|---|
| 8 位 | i8 |
u8 |
-128 ~ 127 / 0 ~ 255 |
| 16 位 | i16 |
u16 |
-32,768 ~ 32,767 / 0 ~ 65,535 |
| 32 位 | i32 |
u32 |
-2³¹ ~ 2³¹-1 / 0 ~ 2³²-1 |
| 64 位 | i64 |
u64 |
-2⁶³ ~ 2⁶³-1 / 0 ~ 2⁶⁴-1 |
| 128 位 | i128 |
u128 |
-2¹²⁷ ~ 2¹²⁷-1 / 0 ~ 2¹²⁸-1 |
| 视架构而定(32位/64位) | isize |
usize |
同 i32/u32 或 i64/u64 |
有符号类型 (iN):
- 范围:
-2^(N-1)~2^(N-1) - 1
无符号类型 (uN):
- 范围:
0~2^N - 1
整型字面量可以用下表的形式书写:
| 数字字面量 | 示例 |
|---|---|
| 十进制 | 98_222 |
| 十六进制 | 0xff |
| 八进制 | 0o77 |
| 二进制 | 0b1111_0000 |
字节 (仅限于 u8) |
b'A' |
Rust 整型默认使用 i32,例如 let i = 1,那 i 就是 i32 类型,因此你可以首选它,同时该类型也往往是性能最好的。isize 和 usize 的主要应用场景是用作集合的索引。
整型溢出
假设有一个 u8 ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生整型溢出。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 panic(崩溃,Rust 使用这个术语来表明程序因错误而退出)。
在当使用 --release 参数进行 release 模式构建时,Rust 不检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(two’s complement wrapping)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 u8 的情况下,256 变成 0,257 变成 1,依此类推。程序不会 panic,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:
- 使用
wrapping_*方法在所有模式下都按照补码循环溢出规则处理,例如wrapping_add - 如果使用
checked_*方法时发生溢出,则返回None值 - 使用
overflowing_*方法返回该值和一个指示是否存在溢出的布尔值 - 使用
saturating_*方法,可以限定计算后的结果不超过目标类型的最大值或低于最小值,例如:
assert_eq!(100u8.saturating_add(1), 101);
assert_eq!(u8::MAX.saturating_add(127), u8::MAX);
下面是一个演示wrapping_*方法的示例:
fn main() {
let a : u8 = 255;
let b = a.wrapping_add(20);
println!("{}", b); // 19
}
浮点类型
浮点类型数字 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: f32 和 f64,分别为 32 位和 64 位大小。默认浮点类型是 f64,在现代的 CPU 中它的速度与 f32 几乎相同,但精度更高。
下面是一个演示浮点数的示例:
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。
浮点陷阱
浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:
- 浮点数往往是你想要数字的近似表达 浮点数类型是基于二进制实现的,但是我们想要计算的数字往往是基于十进制,例如
0.1在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果你想要表达完全精准的真实数字,只有使用无限精度的浮点数才行 - 浮点数在某些特性上是反直觉的 例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用
>,>=等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为f32,f64上的比较运算实现的是std::cmp::PartialEq特征(类似其他语言的接口),但是并没有实现std::cmp::Eq特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:
Rust 的 HashMap 数据结构,是一个 KV 类型的 Hash Map 实现,它对于 K 没有特定类型的限制,但是要求能用作 K 的类型必须实现了 std::cmp::Eq 特征,因此这意味着你无法使用浮点数作为 HashMap 的 Key,来存储键值对,但是作为对比,Rust 的整数类型、字符串类型、布尔类型都实现了该特征,因此可以作为 HashMap 的 Key。
为了避免上面说的两个陷阱,你需要遵守以下准则:
- 避免在浮点数上测试相等性
- 当结果在数学上可能存在未定义时,需要格外的小心。
fn main() {
let abc: (f32, f32, f32) = (0.1, 0.2, 0.3);
let xyz: (f64, f64, f64) = (0.1, 0.2, 0.3);
println!("abc (f32)");
println!(" 0.1 + 0.2: {:x}", (abc.0 + abc.1).to_bits());
println!(" 0.3: {:x}", (abc.2).to_bits());
println!();
println!("xyz (f64)");
println!(" 0.1 + 0.2: {:x}", (xyz.0 + xyz.1).to_bits());
println!(" 0.3: {:x}", (xyz.2).to_bits());
println!();
assert!(abc.0 + abc.1 == abc.2);
assert!(xyz.0 + xyz.1 == xyz.2);
}
// abc (f32)
// 0.1 + 0.2: 3e99999a
// 0.3: 3e99999a
// xyz (f64)
// 0.1 + 0.2: 3fd3333333333334
// 0.3: 3fd3333333333333
// thread 'main' panicked at 'assertion failed: xyz.0 + xyz.1 == xyz.2',
对 f32 类型做加法时,0.1 + 0.2 的结果是 3e99999a,0.3 也是 3e99999a,因此 f32 下的 0.1 + 0.2 == 0.3 通过测试,但是到了 f64 类型时,结果就不一样了,因为 f64 二进制精度高很多,导致了 0.1 + 0.2 并不严格等于 0.3,它们可能在小数点 N 位后存在误差。0.1 + 0.2 以 4 结尾,但是 0.3 以3结尾,这个区别导致 f64 下的测试失败了,并且抛出了异常。
NaN
对于数学上未定义的结果,例如对负数取平方根 -42.1.sqrt() ,会产生一个特殊的结果:Rust 的浮点数类型使用 NaN (not a number) 来处理这些情况。
所有跟 NaN 交互的操作,都会返回一个 NaN,而且 NaN 不能用来比较,下面的代码会崩溃:
fn main() {
let x = (-42.0_f32).sqrt();
assert_eq!(x, x);
}
出于防御性编程的考虑,可以使用 is_nan() 等方法,可以用来判断一个数值是否是 NaN :
fn main() {
let x = (-42.0_f32).sqrt();
if x.is_nan() {
println!("未定义的数学行为")
}
}
数字运算
Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算。
fn main() {
// 三种类型标注方式
let a = 10; // 自动推导为 i32
let b: i32 = 20; // 类型标注
let c = 30i32; // 类型后缀
// 基本运算(类型必须相同)
println!("加法: {} + {} = {}", a, b, a + b);
println!("减法: {} - {} = {}", b, a, b - a);
println!("乘法: {} * {} = {}", a, c, a * c);
println!("除法: {} / {} = {}", c, a, c / a);
println!("取模: {} % {} = {}", b, a, b % a);
// 数字分隔符(提升可读性)
let million: i64 = 1_000_000;
println!("百万的平方: {}", million.pow(2));
// 浮点数运算
let x = 10.0; // 自动推导为 f64
let y = 3.5f32; // 明确指定为 f32
println!("浮点除法: {:.2}", x / 2.0);
println!("f32: {:.3}", y * 2.0);
}
位运算
Rust 的位运算基本上和其他语言一样
| 运算符 | 说明 |
|---|---|
| & 位与 | 相同位置均为1时则为1,否则为0 |
| | 位或 | 相同位置只要有1时则为1,否则为0 |
| ^ 异或 | 相同位置不相同则为1,相同则为0 |
| ! 位非 | 把位中的0和1相互取反,即0置为1,1置为0 |
| << 左移 | 所有位向左移动指定位数,右位补0(会改变符号位) |
| >> 右移 | 所有位向右移动指定位数,行为取决于类型: • 有符号类型(i8/i16/i32等):算术右移,保持符号位 • 无符号类型(u8/u16/u32等):逻辑右移,补0 |
重要说明:
- Rust 没有单独的逻辑右移运算符(如 Java 的
>>>) - 右移
>>的行为完全由操作数的类型决定 -
如需对有符号数进行逻辑右移,可转换为无符号类型后再右移
-
复合赋值:支持
&=、|=、^=、<<=、>>= - 左移溢出:超出类型范围的位会被丢弃
示例
fn main() {
// ========== 基本位运算 ==========
let a = 0b1100u8; // 12 (二进制:1100)
let b = 0b1010u8; // 10 (二进制:1010)
println!("a = {:08b} ({})", a, a);
println!("b = {:08b} ({})", b, b);
println!();
// 位与 &
println!("a & b = {:08b} ({})", a & b, a & b); // 1000 = 8
// 位或 |
println!("a | b = {:08b} ({})", a | b, a | b); // 1110 = 14
// 异或 ^
println!("a ^ b = {:08b} ({})", a ^ b, a ^ b); // 0110 = 6
// 位非 !
println!("!a = {:08b} ({})", !a, !a); // 11110011 = 243
}
关于移位运算:
fn main() {
let neg: i8 = -120; // 10001000
println!("========== 移位操作对比 ==========\n");
println!("测试值: {} = {:08b}\n", neg, neg as u8);
// 右移:核心区别
println!("--- 右移2位 ---");
// 算术右移: -120 >> 2 = -30 = 11100010 (补1)
// 逻辑右移: -120 >>> 2 = 34 = 00100010 (补0)
println!("算术右移: {} >> 2 = {:3} = {:08b} (补1)", neg, neg >> 2, (neg >> 2) as u8);
println!("逻辑右移: {} >>> 2 = {:3} = {:08b} (补0)", neg, (neg as u8) >> 2, (neg as u8) >> 2);
// 左移:完全相同
println!("\n--- 左移2位 ---");
// 算术左移: -120 << 2 = 32 = 00100000
// 逻辑左移: -120 <<< 2 = 32 = 00100000
println!("算术左移: {} << 2 = {:3} = {:08b}", neg, neg << 2, (neg << 2) as u8);
println!("逻辑左移: {} <<< 2 = {:3} = {:08b}", neg, (neg as u8) << 2, (neg as u8) << 2);
}
序列(Range)
Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 1..5,生成从 1 到 4 的连续数字,不包含 5 ;1..=5,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:
for i in 1..=5 {
println!("{}",i);
}
序列只允许用于数字或字符类型,原因是它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。如下是一个使用字符类型序列的例子:
for i in 'a'..='z' {
println!("{}",i);
}
字符类型
Rust 的字符不仅仅是 ASCII,所有的 Unicode 值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode 值的范围从 U+0000 ~ U+D7FF 和 U+E000 ~ U+10FFFF。由于 Unicode 都是 4 个字节编码,因此Rust的字符类型也是占用 4 个字节。
fn main() {
let c = 'z';
let z = 'ℤ';
let g = '国';
let heart_eyed_cat = '😻';
}
布尔类型
Rust 中的布尔类型有两个可能的值:true 和 false,布尔值占用内存的大小为 1 个字节:
fn main() {
let t = true;
let f: bool = false; // 使用类型标注,显式指定f的类型
if f {
println!("这是段毫无意义的代码");
}
}
单元类型
单元类型就是 (),唯一的值也是 () 。
// ✓ 返回单元类型 ()
fn normal() -> () { }
fn main() { }
println!("hello");
// ✓ 发散函数(永不返回)
fn diverge() -> ! {
panic!("crash!");
// 或 loop {}
// 或 std::process::exit(0)
}
| 特性 | 说明 |
|---|---|
| 定义 | () 既是类型也是唯一的值 |
| 大小 | 0 字节,不占内存 |
| main 返回 | 返回 (),不是"无返回值" |
| 用途 | 占位符、表示"不关心返回值" |
| 区别 | -> () 有返回值,-> ! 才是永不返回(发散函数,即没有返回值) |
语句和表达式
Rust 的函数体是由一系列语句组成,最后由一个表达式来返回值,例如:
fn add_with_extra(x: i32, y: i32) -> i32 {
let x = x + 1; // 语句
let y = y + 5; // 语句
x + y // 表达式
}
语句会执行一些操作但是不会返回一个值,而表达式会在求值后返回一个值,因此在上述函数体的三行代码中,前两行是语句,最后一行是表达式。
对于 Rust 语言而言,这种基于语句(statement)和表达式(expression)的方式是非常重要的,你需要能明确的区分这两个概念,但是对于很多其它语言而言,这两个往往无需区分。基于表达式是函数式语言的重要特征,表达式总要返回值。
语句
let a = 8;
let b: Vec<f64> = Vec::new();
let (a, c) = ("hi", false);
以上都是语句,它们完成了一个具体的操作,但是并没有返回值,因此是语句。
由于 let 是语句,因此不能将 let 语句赋值给其它值,如下形式是错误的:
let b = (let a = 8);// variable declaration using `let` is a statement `let`是一条语句
表达式
表达式会进行求值,然后返回一个值。例如 5 + 6,在求值后,返回值 11,因此它就是一条表达式。
表达式可以成为语句的一部分,例如 let y = 6 中,6 就是一个表达式,它在求值后返回一个值 6。调用一个函数是表达式,因为会返回一个值,调用宏也是表达式,用花括号包裹最终返回一个值的语句块也是表达式,总之,能返回值,它就是表达式:
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y); // 4
}
上面使用一个语句块表达式将值赋给 y 变量,语句块长这样:
{
let x = 3;
x + 1
}
该语句块是表达式的原因是:它的最后一行是表达式,返回了 x + 1 的值,注意 x + 1 不能以分号结尾,否则就会从表达式变成语句, 表达式不能包含分号。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也不会返回一个值,请牢记!
最后,表达式如果不返回任何值,会隐式地返回一个 () 。
fn main() {
assert_eq!(ret_unit_type(), ())
}
fn ret_unit_type() {
let x = 1;
// if 语句块也是一个表达式,因此可以用于赋值,也可以直接返回
// 类似三元运算符,在Rust里我们可以这样写
let y = if x % 2 == 1 {
"odd"
} else {
"even"
};
// 或者写成一行
let z = if x % 2 == 1 { "odd" } else { "even" };
}
函数
以 add 函数为例,声明函数的关键字 fn,函数名 add(),参数 i: i32 和 j: i32,参数类型和返回值类型都是 i32。
fn add(i: i32, j: i32) -> i32 {
i + j
}

函数要点
- 函数名和变量名使用蛇形命名法
(snake_case),例如fn add_two() {} - 函数的位置可以随便放,Rust 不关心我们在哪里定义了函数,只要有定义即可
- 每个函数参数都需要标注类型
函数参数
Rust 是静态类型语言,因此需要你为每一个函数参数都标识出它的具体类型,例如:
fn main() {
another_function(5, 6.1);
}
fn another_function(x: i32, y: f32) {
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
another_function 函数有两个参数,其中 x 是 i32 类型,y 是 f32 类型,然后在该函数内部,打印出这两个值。这里去掉 x 或者 y 的任何一个的类型,都会报错。
函数返回
在 Rust 中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。
函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用 return 提前返回,下面的函数使用最后一条表达式来返回一个值:
fn plus_number(x:i32) -> i32 {
if x == 5 {
return 10;
}
else {
x + 10
}
}
fn main() {
let x = plus_number(6);
println!("The value of x is: {}", x);
}
返回单元类型
单元类型 (),是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值:
- 函数没有返回值,那么返回一个
() - 通过
;结尾的语句返回一个()
fn plus_number(x:i32) {
x + 5;
}
// 或
fn plus_number(x:i32) -> () {
x + 5;
}
永不返回的发散函数 !
当用 ! 作函数返回类型的时候,表示该函数永不返回( diverging functions ),特别的,这种语法往往用做会导致程序崩溃的函数:
fn dead_end() -> ! {
panic!("dead_end");
}
下面的函数创建了一个无限循环,该循环永不跳出,因此函数也永不返回:
fn forever() -> ! {
loop {
//...
};
}
所有权和借用
所有权
Rust 通过所有权来管理内存,这种检查只发生在编译期,因此对于程序运行期,不会有性能上的损失。
- Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
- 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
- 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
变量作用域
作用域是一个变量在程序中有效的范围,假如有这样一个变量:
let s = "hello";
变量 s 绑定到了一个字符串字面值,该字符串字面值是硬编码到程序代码中的。s 变量从声明的点开始直到当前作用域的结束都是有效的:
{ // s 在这里无效,它尚未声明
let s = "hello"; // 从此处起,s 是有效的
// 使用 s
} // 此作用域已结束,s不再有效
简而言之,s 从创建开始就有效,然后有效期持续到它离开作用域为止,可以看出,就作用域来说,Rust 语言跟其他编程语言没有区别。
所有权转移
先来看一段代码:
let x = 5;
let y = x;
这段代码并没有发生所有权的转移,原因很简单: 代码首先将 5 绑定到变量 x,接着拷贝 x 的值赋给 y,最终 x 和 y 都等于 5,因为整数是 Rust 基本数据类型,是固定大小的简单值,因此这两个值都是通过自动拷贝的方式来赋值的,都被存在栈中,完全无需在堆上分配内存。整个过程中的赋值都是通过值拷贝的方式完成(发生在栈中),因此并不需要所有权转移。
移动(move)
然后再来看一段代码:
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
}
String 类型是一个复杂类型,由存储在栈中的堆指针、字符串长度、字符串容量共同组成。String 类型指向了一个堆上的空间,这里存储着它的真实数据。当 s1 被赋予 s2 后,Rust 认为 s1 不再有效,因此也无需在 s1 离开作用域后 drop 任何东西,这就是把所有权从 s1 转移给了 s2,s1 在被赋予 s2 后就马上失效了。
pub struct String {
vec: Vec<u8>, // 内部使用Vec<u8>
}
// Vec<u8> 的结构
pub struct Vec<T> {
ptr: *mut T, // 指向堆数据的指针 (8字节 on 64-bit)
len: usize, // 当前长度 (8字节 on 64-bit)
cap: usize, // 容量 (8字节 on 64-bit)
}
深拷贝(clone)
首先,Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何自动的复制都不是深拷贝,可以被认为对运行时性能影响较小。
如果我们确实需要深度复制 String 中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 clone 的方法。
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
这段代码能够正常运行,说明 s2 确实完整的复制了 s1 的数据。
如果代码性能无关紧要,例如初始化程序时或者在某段时间只会执行寥寥数次时,你可以使用 clone 来简化编程。但是对于执行较为频繁的代码(热点路径),使用 clone 会极大的降低程序性能,需要小心使用!
浅拷贝(copy)
关键点:
Copy类型在赋值时会按位复制(栈上复制)- 实现
Copy的类型不能拥有堆上的数据Copy是隐式的,Clone是显式的
Rust 有一个叫做 Copy 的特征,可以用在类似整型这样在栈中存储的类型。如果一个类型拥有 Copy 特征,一个旧的变量在被赋值给其他变量后仍然可用,也就是赋值的过程即是拷贝的过程。
如下是一些 Copy 的类型:
- 所有整数类型,比如
u32 - 布尔类型,
bool,它的值是true和false - 所有浮点数类型,比如
f64 - 字符类型,
char - 元组,当且仅当其包含的类型也都是
Copy的时候。比如,(i32, i32)是Copy的,但(i32, String)就不是 - 不可变引用
&T,但是注意:可变引用&mut T是不可以 Copy的。
一个类型可以是 Copy 的,当且仅当:
- ✅ 所有字段都实现了
Copy - ✅ 不拥有任何堆上的资源
- ✅ 没有实现
Droptrait - ✅ 是固定大小的(
Sized)
函数传值和返回
将值传递给函数,一样会发生 移动 或者 复制,就跟 let 语句一样,下面的代码展示了所有权、作用域的规则:
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里 ...
// ... 所以到这里不再有效
let x = 5; // x 进入作用域
makes_copy(x); // x 应该移动函数里,
// 但 i32 是 Copy 的,所以在后面可继续使用 x
} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
// 所以不会有特殊操作
fn takes_ownership(some_string: String) { // some_string 进入作用域
println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
fn makes_copy(some_integer: i32) { // some_integer 进入作用域
println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作
同样的,函数返回值也有所有权,例如:
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值
// 移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 移出作用域并被丢弃
fn gives_ownership() -> String { // gives_ownership 将返回值移动给
// 调用它的函数
let some_string = String::from("hello"); // some_string 进入作用域.
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}
所有权很强大,避免了内存的不安全性,但是也带来了一个新麻烦: 总是把一个值传来传去来使用它。 传入一个函数,很可能还要从该函数传出去,结果就是语言表达变得非常啰嗦,幸运的是,Rust 提供了新功能解决这个问题。
引用和借用
引用与解引用
常规引用是一个指针类型,指向了对象存储的内存地址。在下面代码中,我们创建一个 i32 值的引用 y,然后使用解引用运算符来解出 y 所使用的值:
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
变量 x 存放了一个 i32 值 5。y 是 x 的一个引用。可以断言 x 等于 5。然而,如果希望对 y 的值做出断言,必须使用 *y 来解出引用所指向的值(也就是解引用)。一旦解引用了 y,就可以访问 y 所指向的整型值并可以与 5 做比较。
不可变引用
下面的代码,我们用 s1 的引用作为参数传递给 calculate_length 函数,而不是把 s1 的所有权转移给该函数:
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
// 函数 calculate_length 使用 & 来表明参数 s 的类型是一个引用:
fn calculate_length(s: &String) -> usize {
s.len()
}
这里,& 符号即是引用,它们允许你使用值,但是不获取所有权。通过 &s1 语法,我们创建了一个指向 s1 的引用,但是并不拥有它。因为并不拥有这个值,当引用离开作用域后,其指向的值也不会被丢弃。
可变引用
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
首先,声明 s 是可变类型,其次创建一个可变的引用 &mut s 和接受可变引用参数 some_string: &mut String 的函数。可变引用同时只能存在一个,可变引用并不是随心所欲、想用就用的,它有一个很大的限制: 同一作用域,特定数据只能有一个可变引用:
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
error[E0499]: cannot borrow `s` as mutable more than once at a time 同一时间无法对 `s` 进行两次可变借用
--> src/main.rs:5:14
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here 首个可变引用在这里借用
5 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here 第二个可变引用在这里借用
6 |
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here 第一个借用在这里使用
在数据被可变借用期间,原所有者完全失去对数据的访问权,既不能读也不能写。
let mut s = String::from("hello");
let r1 = &mut s;
s.push_str(", world!");
println!("{}", r1);
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> src/main.rs:5:5
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here
5 | s.push_str(", world!");
| ^ second mutable borrow occurs here
6 |
7 | println!("{}", r1);
| -- first borrow later used here
NLL(非词法生命周期)
| 概念 | 何时结束 | 说明 |
|---|---|---|
| 借用的生命周期 | 最后一次使用后 | NLL 智能推断 |
| 值的所有权 | 作用域 } 结束 |
drop 时机 |
复合类型
字符串
Rust 中的字符串主要有两种核心类型:String 和 &str。
两种核心字符串类型
&str(字符串切片)
// 字符串字面量,存储在程序的只读内存区
let s1: &str = "Hello, Rust!";
// 也可以是 String 的切片引用
let string = String::from("Hello");
let s2: &str = &string[0..3]; // "Hel"
特点: - 不可变引用(borrowed) - 固定大小的视图 - 通常作为函数参数使用
String(可增长字符串)
// 创建空字符串
let mut s = String::new();
// 从字面量创建
let s = String::from("Hello");
let s = "Hello".to_string();
// 带容量预分配
let s = String::with_capacity(100);
特点: - 堆上分配,可增长 - 拥有所有权(owned) - 可修改
内存布局对比
┌─────────────────────────────────────────────────────────────────────┐
│ String 内存布局 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 栈上 (Stack) 堆上 (Heap) │
│ ┌────────────────┐ ┌───┬───┬───┬───┬───┐ │
│ │ ptr ──────────────────────────►│ H │ e │ l │ l │ o │ │
│ ├────────────────┤ └───┴───┴───┴───┴───┘ │
│ │ len = 5 │ │
│ ├────────────────┤ │
│ │ capacity = 8 │ │
│ └────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ &str 内存布局 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 栈上 (Stack) 只读内存/堆 │
│ ┌────────────────┐ ┌───┬───┬───┬───┬───┐ │
│ │ ptr ──────────────────────────►│ H │ e │ l │ l │ o │ │
│ ├────────────────┤ └───┴───┴───┴───┴───┘ │
│ │ len = 5 │ (胖指针: ptr + len) │
│ └────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
String 常用操作
创建与修改
fn main() {
let mut s = String::from("Hello");
// 追加字符串
s.push_str(", World"); // "Hello, World"
// 追加单个字符
s.push('!'); // "Hello, World!"
// 插入
s.insert(0, '🎉'); // "🎉Hello, World!"
s.insert_str(1, " "); // "🎉 Hello, World!"
// 替换
let new_s = s.replace("World", "Rust");
// 删除
s.pop(); // 移除最后一个字符
s.remove(0); // 移除指定索引的字符(按字节)
s.truncate(5); // 截断到指定长度
s.clear(); // 清空
}
字符串连接
fn main() {
let s1 = String::from("Hello");
let s2 = String::from("World");
// 方式1: + 运算符 (注意所有权转移)
let s3 = s1 + " " + &s2; // s1 被移动,不能再使用
// 方式2: format! 宏 (推荐,不转移所有权)
let s1 = String::from("Hello");
let s4 = format!("{} {} {}", s1, s2, "Rust");
// 方式3: push_str
let mut s5 = String::from("Hello");
s5.push_str(" World");
// 方式4: concat (适用于数组/切片)
let s6 = ["Hello", " ", "World"].concat();
// 方式5: join
let s7 = ["Hello", "World"].join(" "); // "Hello World"
}
UTF-8 编码特性
Rust 字符串强制使用 UTF-8 编码,这带来一些特殊行为:
字符与字节的区别
fn main() {
let s = "你好Rust";
println!("字节长度: {}", s.len()); // 10 (中文3字节×2 + 4字节)
println!("字符数量: {}", s.chars().count()); // 6
// 三种视图
// 字节视图
for b in s.bytes() {
print!("{} ", b); // 228 189 160 229 165 189 82 117 115 116
}
// 字符视图
for c in s.chars() {
print!("{} ", c); // 你 好 R u s t
}
// 字形簇 (需要外部 crate)
// use unicode_segmentation::UnicodeSegmentation;
// for g in s.graphemes(true) { ... }
}
索引限制
fn main() {
let s = String::from("你好");
// ❌ 编译错误!不能直接索引
// let c = s[0];
// ✅ 正确方式:使用切片(必须在有效字符边界)
let slice = &s[0..3]; // "你" (3字节)
// ❌ 运行时 panic!无效的字符边界
// let bad = &s[0..2]; // 截断了UTF-8字符
// ✅ 安全的获取字符
let first_char = s.chars().nth(0); // Some('你')
}
类型转换
fn main() {
// &str -> String
let s: String = "hello".to_string();
let s: String = String::from("hello");
let s: String = "hello".to_owned();
let s: String = "hello".into();
// String -> &str
let string = String::from("hello");
let slice: &str = &string; // 解引用强制转换
let slice: &str = string.as_str(); // 显式转换
let slice: &str = &string[..]; // 完整切片
// String <-> Vec<u8>
let s = String::from("hello");
let bytes: Vec<u8> = s.into_bytes();
let s = String::from_utf8(bytes).unwrap();
// &str <-> &[u8]
let s = "hello";
let bytes: &[u8] = s.as_bytes();
let s = std::str::from_utf8(bytes).unwrap();
// 数字转字符串
let n = 42;
let s = n.to_string();
let s = format!("{}", n);
// 字符串转数字
let n: i32 = "42".parse().unwrap();
let n = "42".parse::<i32>().unwrap();
}
Deref 强制转换
// String 实现了 Deref<Target=str>
// 这意味着 String 可以自动转换为 &str
fn print_str(s: &str) {
println!("{}", s);
}
fn main() {
let owned = String::from("Hello");
let borrowed = "World";
print_str(&owned); // ✅ &String 自动转为 &str
print_str(borrowed); // ✅ &str
// 因此函数参数推荐使用 &str,更通用
}
性能与实践
容量预分配
fn main() {
// ❌ 频繁扩容
let mut s = String::new();
for i in 0..1000 {
s.push_str(&i.to_string());
}
// ✅ 预分配容量
let mut s = String::with_capacity(4000);
for i in 0..1000 {
s.push_str(&i.to_string());
}
println!("Capacity: {}", s.capacity());
}
避免不必要的克隆
// ❌ 不必要的克隆
fn process(s: String) { /* ... */ }
let s = String::from("hello");
process(s.clone()); // 如果之后不用 s,这是浪费
// ✅ 如果只需要读取,用引用
fn process_ref(s: &str) { /* ... */ }
process_ref(&s);
// ✅ 使用 Cow 实现按需克隆
use std::borrow::Cow;
fn maybe_modify(s: &str) -> Cow<str> {
if s.contains("old") {
Cow::Owned(s.replace("old", "new"))
} else {
Cow::Borrowed(s)
}
}
函数参数建议
// ✅ 推荐:接受 &str,兼容性最好
fn greet(name: &str) {
println!("Hello, {}", name);
}
// 可接受 &str 和 &String
greet("World");
greet(&String::from("Rust"));
// 需要所有权时才用 String
fn take_ownership(s: String) {
// ...
}
常见陷阱总结
| 陷阱 | 问题 | 解决方案 |
|---|---|---|
| 直接索引 | s[0] 编译错误 |
使用 s.chars().nth(0) |
| 无效切片边界 | &s[0..2] panic(中文) |
确保在字符边界切分 |
+ 运算符 |
左侧所有权被移动 | 使用 format! 或 clone() |
| 比较长度 | len() 返回字节数 |
使用 chars().count() |
| 空字符串检查 | 多种方式 | 优先用 is_empty() |
切片
切片(Slice)是 Rust 中非常核心的概念,它是对连续内存序列的一个引用视图,不拥有数据所有权。
切片基本概念
什么是切片
fn main() {
let arr = [1, 2, 3, 4, 5];
// 切片:对数组部分或全部元素的引用
let slice: &[i32] = &arr[1..4]; // [2, 3, 4]
println!("{:?}", slice);
}
切片的本质
┌─────────────────────────────────────────────────────────────────────┐
│ 切片是"胖指针" │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 普通指针 (8 bytes) 切片/胖指针 (16 bytes) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ ptr │ │ ptr │ ──► 指向数据起始位置 │
│ └──────────────┘ ├──────────────┤ │
│ │ len │ ──► 元素数量 │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
切片的内存布局
fn main() {
let arr: [i32; 5] = [10, 20, 30, 40, 50];
let slice: &[i32] = &arr[1..4];
println!("数组地址: {:p}", &arr);
println!("切片指向: {:p}", slice.as_ptr());
println!("切片长度: {}", slice.len());
println!("切片大小: {} bytes", std::mem::size_of_val(&slice));
}
内存示意图:
arr (栈上连续内存):
┌──────┬──────┬──────┬──────┬──────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└──────┴──────┴──────┴──────┴──────┘
[0] [1] [2] [3] [4]
▲ │
│ │
└─────────────┘
slice 覆盖范围 [1..4]
slice (胖指针):
┌─────────────────┐
│ ptr ──────────────► 指向 arr[1] 的地址
├─────────────────┤
│ len = 3 │
└─────────────────┘
切片语法详解
Range 语法
fn main() {
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// 各种切片范围
let a = &arr[2..5]; // [2, 3, 4] 半开区间 [2, 5)
let b = &arr[2..=5]; // [2, 3, 4, 5] 闭区间 [2, 5]
let c = &arr[..4]; // [0, 1, 2, 3] 从开头到索引4(不含)
let d = &arr[6..]; // [6, 7, 8, 9] 从索引6到结尾
let e = &arr[..]; // 全部元素 完整切片
// 等价写法
let full = &arr[0..arr.len()]; // 与 &arr[..] 相同
println!("a: {:?}", a);
println!("b: {:?}", b);
println!("c: {:?}", c);
println!("d: {:?}", d);
println!("e: {:?}", e);
}
范围类型对照表
| 语法 | 类型 | 含义 | 示例结果 |
|---|---|---|---|
start..end |
Range |
[start, end) | [1..4] → 索引1,2,3 |
start..=end |
RangeInclusive |
[start, end] | [1..=4] → 索引1,2,3,4 |
start.. |
RangeFrom |
[start, 末尾] | [2..] → 从索引2到末尾 |
..end |
RangeTo |
[0, end) | [..3] → 索引0,1,2 |
..=end |
RangeToInclusive |
[0, end] | [..=3] → 索引0,1,2,3 |
.. |
RangeFull |
全部 | [..] → 所有元素 |
常见切片类型
数组切片 &[T]
fn main() {
// 从数组创建
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let slice: &[i32] = &arr[..];
// 从 Vec 创建
let vec: Vec<i32> = vec![1, 2, 3, 4, 5];
let slice: &[i32] = &vec[1..4];
// 从另一个切片创建
let sub_slice: &[i32] = &slice[0..2];
println!("{:?}", sub_slice);
}
字符串切片 &str
fn main() {
// 字符串字面量本身就是 &str
let s1: &str = "Hello, World!";
// 从 String 创建
let string = String::from("Hello, Rust!");
let s2: &str = &string[0..5]; // "Hello"
// 注意:必须在有效 UTF-8 边界切分
let chinese = "你好世界";
let s3 = &chinese[0..3]; // "你" (一个中文字符占3字节)
// let bad = &chinese[0..2]; // panic! 无效边界
println!("{}", s3);
}
可变切片 &mut [T]
fn main() {
let mut arr = [1, 2, 3, 4, 5];
// 创建可变切片
let slice: &mut [i32] = &mut arr[1..4];
// 通过可变切片修改原数据
slice[0] = 20;
slice[1] = 30;
slice[2] = 40;
println!("{:?}", arr); // [1, 20, 30, 40, 5]
}
切片与所有权类型的关系
┌─────────────────────────────────────────────────────────────────────┐
│ 所有权类型 vs 借用类型 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Owned │ │ Borrowed │ │
│ │ (拥有数据) │ ──Deref──► │ (借用视图) │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ String │ ──────────────► │ &str │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Vec<T> │ ──────────────► │ &[T] │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ [T; N] │ ──────────────► │ &[T] │ │
│ │ (数组) │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
fn main() {
// Vec<T> -> &[T]
let vec: Vec<i32> = vec![1, 2, 3];
let slice: &[i32] = &vec; // 自动 Deref
let slice: &[i32] = vec.as_slice(); // 显式转换
let slice: &[i32] = &vec[..]; // 范围切片
// [T; N] -> &[T]
let arr: [i32; 3] = [1, 2, 3];
let slice: &[i32] = &arr; // 自动转换
let slice: &[i32] = &arr[..]; // 范围切片
// String -> &str
let string: String = String::from("hello");
let slice: &str = &string; // 自动 Deref
let slice: &str = string.as_str(); // 显式转换
}
切片常用方法
基础方法
fn main() {
let slice: &[i32] = &[10, 20, 30, 40, 50];
// 长度与判空
println!("长度: {}", slice.len()); // 5
println!("是否为空: {}", slice.is_empty()); // false
// 获取元素
println!("第一个: {:?}", slice.first()); // Some(&10)
println!("最后一个: {:?}", slice.last()); // Some(&50)
println!("索引2: {:?}", slice.get(2)); // Some(&30)
println!("索引10: {:?}", slice.get(10)); // None (安全访问)
// 直接索引(可能 panic)
println!("slice[2] = {}", slice[2]); // 30
// println!("{}", slice[10]); // panic!
}
迭代与遍历
fn main() {
let slice = &[1, 2, 3, 4, 5];
// 不可变迭代
for item in slice.iter() {
print!("{} ", item);
}
println!();
// 带索引迭代
for (index, item) in slice.iter().enumerate() {
println!("[{}] = {}", index, item);
}
// 可变迭代
let mut arr = [1, 2, 3, 4, 5];
let slice = &mut arr[..];
for item in slice.iter_mut() {
*item *= 2;
}
println!("{:?}", arr); // [2, 4, 6, 8, 10]
}
查找与搜索
fn main() {
let slice = &[3, 1, 4, 1, 5, 9, 2, 6];
// 包含检查
println!("包含 4: {}", slice.contains(&4)); // true
// 查找位置
println!("4 的位置: {:?}", slice.iter().position(|&x| x == 4)); // Some(2)
// 开头/结尾检查
println!("以 [3,1] 开头: {}", slice.starts_with(&[3, 1])); // true
println!("以 [2,6] 结尾: {}", slice.ends_with(&[2, 6])); // true
// 二分查找(需要已排序)
let sorted = &[1, 2, 3, 4, 5, 6, 7, 8, 9];
println!("二分查找 5: {:?}", sorted.binary_search(&5)); // Ok(4)
println!("二分查找 10: {:?}", sorted.binary_search(&10)); // Err(9)
}
分割与连接
fn main() {
let slice = &[1, 2, 3, 4, 5, 6, 7, 8];
// 分成两半
let (left, right) = slice.split_at(4);
println!("左: {:?}, 右: {:?}", left, right); // [1,2,3,4], [5,6,7,8]
// 按条件分割
let parts: Vec<&[i32]> = slice.split(|&x| x % 3 == 0).collect();
println!("{:?}", parts); // [[1, 2], [4, 5], [7, 8]]
// 分块 (chunks)
for chunk in slice.chunks(3) {
println!("块: {:?}", chunk);
}
// 块: [1, 2, 3]
// 块: [4, 5, 6]
// 块: [7, 8]
// 固定大小窗口
for window in slice.windows(3) {
println!("窗口: {:?}", window);
}
// 窗口: [1, 2, 3]
// 窗口: [2, 3, 4]
// 窗口: [3, 4, 5]
// ...
}
排序与反转(需要可变切片)
fn main() {
let mut arr = [3, 1, 4, 1, 5, 9, 2, 6];
let slice = &mut arr[..];
// 排序
slice.sort();
println!("排序后: {:?}", slice); // [1, 1, 2, 3, 4, 5, 6, 9]
// 反转
slice.reverse();
println!("反转后: {:?}", slice); // [9, 6, 5, 4, 3, 2, 1, 1]
}
复制与填充
fn main() {
let src = [1, 2, 3];
let mut dst = [0; 5];
// 复制切片
dst[..3].copy_from_slice(&src);
println!("{:?}", dst); // [1, 2, 3, 0, 0]
// 填充
dst.fill(7);
println!("{:?}", dst); // [7, 7, 7, 7, 7]
// 用迭代器填充
dst.fill_with(|| 42);
println!("{:?}", dst); // [42, 42, 42, 42, 42]
// 交换元素
let mut arr = [1, 2, 3, 4, 5];
arr.swap(0, 4);
println!("{:?}", arr); // [5, 2, 3, 4, 1]
}
函数参数中的切片
切片作为参数
// ✅ 推荐:接受切片,更通用
fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
// ❌ 不推荐:只能接受特定类型
fn sum_vec(numbers: &Vec<i32>) -> i32 {
numbers.iter().sum()
}
fn main() {
let arr = [1, 2, 3, 4, 5];
let vec = vec![1, 2, 3, 4, 5];
// 切片参数可以接受多种来源
println!("数组求和: {}", sum(&arr)); // ✅
println!("Vec求和: {}", sum(&vec)); // ✅
println!("部分求和: {}", sum(&arr[1..4])); // ✅
println!("部分求和: {}", sum(&vec[..3])); // ✅
}
可变切片参数
fn double_values(slice: &mut [i32]) {
for item in slice.iter_mut() {
*item *= 2;
}
}
fn main() {
let mut arr = [1, 2, 3, 4, 5];
// 修改部分
double_values(&mut arr[1..4]);
println!("{:?}", arr); // [1, 4, 6, 8, 5]
// 修改全部
double_values(&mut arr);
println!("{:?}", arr); // [2, 8, 12, 16, 10]
}
返回切片
// 返回切片必须关联生命周期
fn first_half(slice: &[i32]) -> &[i32] {
let mid = slice.len() / 2;
&slice[..mid]
}
// 返回可变切片
fn first_half_mut(slice: &mut [i32]) -> &mut [i32] {
let mid = slice.len() / 2;
&mut slice[..mid]
}
fn main() {
let arr = [1, 2, 3, 4, 5, 6];
let half = first_half(&arr);
println!("{:?}", half); // [1, 2, 3]
}
元组
元组是 Rust 中一种重要的复合类型,可以将多个不同类型的值组合成一个单一的复合值。
元组基本概念
什么是元组
fn main() {
// 元组:固定长度、可包含不同类型的值的集合
let tuple: (i32, f64, char, &str) = (42, 3.14, 'R', "Rust");
println!("{:?}", tuple); // (42, 3.14, 'R', "Rust")
}
元组的特点
┌─────────────────────────────────────────────────────────────────────┐
│ 元组特性 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ✓ 固定长度 - 一旦声明,长度不可改变 │
│ ✓ 异构类型 - 每个元素可以是不同类型 │
│ ✓ 有序集合 - 元素按位置索引访问 │
│ ✓ 值类型 - 存储在栈上(如果所有元素都是栈类型) │
│ ✓ 可解构 - 支持模式匹配解构 │
│ │
└─────────────────────────────────────────────────────────────────────┘
元组的创建与类型
基本创建方式
fn main() {
// 显式类型标注
let t1: (i32, f64, bool) = (100, 2.5, true);
// 类型推断
let t2 = (100, 2.5, true); // (i32, f64, bool)
// 单元素元组(注意逗号)
let t3: (i32,) = (42,); // 这是元组
let not_tuple = (42); // 这只是 i32,不是元组!
// = note: `#[warn(unused_parens)]` (part of `#[warn(unused)]`) on by default
// help: remove these parentheses
// |
// 3 - let _not_tuple = (42); // 这只是 i32,不是元组!
// 3 + let _not_tuple = 42 ; // 这只是 i32,不是元组!
// 空元组(单元类型)
let unit: () = ();
// 嵌套元组
let nested: ((i32, i32), (f64, f64)) = ((1, 2), (3.0, 4.0));
println!("t1: {:?}", t1);
println!("t3: {:?}", t3);
println!("nested: {:?}", nested);
}
元组类型签名
fn main() {
// 每种元素组合都是独特的类型
let a: (i32, i32) = (1, 2);
let b: (i32, i64) = (1, 2); // 不同类型!
let c: (i64, i32) = (1, 2); // 顺序不同也是不同类型!
// 以下会编译错误:类型不匹配
// let d: (i32, i32) = (1, 2, 3); // 长度不同
// let e: (i32, i32) = b; // 元素类型不同
}
内存布局
fn main() {
let tuple: (u8, u32, u8) = (1, 2, 3);
println!("元组大小: {} bytes", std::mem::size_of_val(&tuple));
println!("元组对齐: {} bytes", std::mem::align_of_val(&tuple));
}
┌─────────────────────────────────────────────────────────────────────┐
│ (u8, u32, u8) 实际内存布局 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 声明顺序: (u8, u32, u8) │
│ 理论大小: 1 + 3(padding) + 4 + 1 + 3(padding) = 12 bytes │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ 实际布局(Rust 编译器重排优化后): │
│ │
│ ┌────────────────────────────┬──────┬──────┬──────────────┐ │
│ │ u32 │ u8 │ u8 │ padding │ │
│ │ 4 bytes │ 1B │ 1B │ 2 bytes │ │
│ └────────────────────────────┴──────┴──────┴──────────────┘ │
│ offset: 0 4 5 6 8 │
│ │
│ 实际大小: 4 + 1 + 1 + 2(padding) = 8 bytes │
│ 对齐要求: 4 bytes (最大成员 u32 的对齐) │
│ │
└─────────────────────────────────────────────────────────────────────┘
// 使用 #[repr(C)] 按 C 语言布局
#[repr(C)]
struct TupleC(u8, u32, u8);
// 默认布局(Rust 重排)
struct TupleRust(u8, u32, u8);
fn main() {
println!("repr(C) 大小: {} bytes", std::mem::size_of::<TupleC>()); // 12
println!("repr(Rust) 大小: {} bytes", std::mem::size_of::<TupleRust>()); // 8
// repr(C) 严格按声明顺序,不优化
// ┌────┬─────────┬────────────────┬────┬─────────┐
// │ u8 │ padding │ u32 │ u8 │ padding │
// │ 1B │ 3B │ 4B │ 1B │ 3B │
// └────┴─────────┴────────────────┴────┴─────────┘
// 总共: 1 + 3 + 4 + 1 + 3 = 12 bytes
}
访问元组元素
索引访问(点号语法)
fn main() {
let tuple = (500, 6.4, "hello", true);
// 使用 .索引 访问(索引从 0 开始)
let first = tuple.0; // 500
let second = tuple.1; // 6.4
let third = tuple.2; // "hello"
let fourth = tuple.3; // true
println!("第一个元素: {}", first);
println!("第二个元素: {}", second);
println!("第三个元素: {}", third);
println!("第四个元素: {}", fourth);
// 注意:索引必须是编译期常量
// let index = 0;
// let value = tuple.index; // ❌ 编译错误!
}
解构(Destructuring)
fn main() {
let tuple = (1, 2.0, "three");
// 完全解构
let (x, y, z) = tuple;
println!("x={}, y={}, z={}", x, y, z);
// 部分解构(使用 _ 忽略不需要的值)
let (a, _, c) = tuple;
println!("a={}, c={}", a, c);
// 忽略多个值
let (first, ..) = tuple;
println!("first={}", first);
// 获取首尾
let tuple5 = (1, 2, 3, 4, 5);
let (head, .., tail) = tuple5;
println!("head={}, tail={}", head, tail);
// 嵌套解构
let nested = ((1, 2), (3, 4));
let ((a, b), (c, d)) = nested;
println!("a={}, b={}, c={}, d={}", a, b, c, d);
}
可变元组
fn main() {
// 声明可变元组
let mut tuple = (1, 2, 3);
// 修改单个元素
tuple.0 = 100;
tuple.1 = 200;
println!("{:?}", tuple); // (100, 200, 3)
// 通过解构修改
let (ref mut a, ref mut b, _) = tuple;
*a = 1000;
*b = 2000;
println!("{:?}", tuple); // (1000, 2000, 3)
}
单元类型 ()
我们之间讲到的单元类型也是一种特殊的元组。
单元类型的概念
fn main() {
// () 是一种特殊的元组,称为单元类型(unit type)
let unit: () = ();
// 单元类型的大小为 0
println!("() 的大小: {} bytes", std::mem::size_of::<()>()); // 0
// 单元类型只有一个值,也是 ()
assert_eq!(unit, ());
}
单元类型的用途
// 1. 函数无返回值时,实际返回 ()
fn no_return() {
println!("这个函数没有显式返回值");
}
fn explicit_unit() -> () {
println!("显式声明返回 ()");
}
// 2. 表达式语句的值
fn main() {
let result = no_return(); // result 的类型是 ()
println!("result: {:?}", result);
// 语句的值是 ()
let x = {
let y = 5;
// 没有返回表达式,块的值是 ()
};
println!("x: {:?}", x);
// 赋值表达式的值是 ()
let mut a = 0;
let b = (a = 5); // b 是 ()
println!("b: {:?}", b);
}
// 3. 泛型中作为占位符
struct Container<T> {
value: T,
}
fn main() {
// 当不需要存储值时,使用 ()
let empty: Container<()> = Container { value: () };
}
// 4. Result 中表示无有意义的返回值
fn might_fail() -> Result<(), String> {
// 成功时返回 Ok(())
Ok(())
}
元组与函数
作为函数参数
// 接受元组参数
fn print_point(point: (i32, i32)) {
println!("Point: ({}, {})", point.0, point.1);
}
// 解构参数
fn print_point_destructured((x, y): (i32, i32)) {
println!("Point: ({}, {})", x, y);
}
fn main() {
let p = (10, 20);
print_point(p);
print_point_destructured(p);
}
作为返回值(多返回值)
// 返回多个值
fn min_max(numbers: &[i32]) -> Option<(i32, i32)> {
if numbers.is_empty() {
return None;
}
let mut min = numbers[0];
let mut max = numbers[0];
for &num in numbers.iter().skip(1) {
if num < min { min = num; }
if num > max { max = num; }
}
Some((min, max))
}
// 返回带状态的结果
fn divide_with_remainder(dividend: i32, divisor: i32) -> (i32, i32) {
let quotient = dividend / divisor;
let remainder = dividend % divisor;
(quotient, remainder)
}
fn main() {
// 使用多返回值
if let Some((min, max)) = min_max(&[3, 1, 4, 1, 5, 9, 2, 6]) {
println!("最小值: {}, 最大值: {}", min, max);
}
let (quotient, remainder) = divide_with_remainder(17, 5);
println!("17 / 5 = {} 余 {}", quotient, remainder);
}
交换值的惯用法
fn main() {
let mut a = 1;
let mut b = 2;
println!("交换前: a={}, b={}", a, b);
// 使用元组交换值(Rust 惯用法)
(a, b) = (b, a);
println!("交换后: a={}, b={}", a, b);
// 也可以用 std::mem::swap
std::mem::swap(&mut a, &mut b);
println!("再次交换: a={}, b={}", a, b);
}
结构体
结构体是 Rust 中创建自定义数据类型的基本方式,将多个相关值组合成一个整体。
结构体形式
// 1. 常规结构体 - 有命名字段(最常用)
struct User {
username: String,
email: String,
age: u32,
active: bool,
}
// 2. 元组结构体 - 字段无名称,通过索引访问
struct Color(u8, u8, u8);
struct Point(f64, f64);
struct UserId(u64); // New Type 模式
// 3. 单元结构体 - 无字段,大小为 0
struct Marker;
fn main() {
// 创建常规结构体
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: 25,
active: true,
};
// 创建元组结构体
let red = Color(255, 0, 0);
let origin = Point(0.0, 0.0);
let id = UserId(12345);
// 创建单元结构体
let marker = Marker;
// 访问字段
println!("用户名: {}", user.username); // 常规结构体用字段名
println!("红色值: {}", red.0); // 元组结构体用索引
println!("用户ID: {}", id.0);
println!("Marker大小: {} bytes", std::mem::size_of::<Marker>()); // 0
}
┌────────────────┬─────────────────┬─────────────────┬──────────────────┐
│ 特性 │ 常规结构体 │ 元组结构体 │ 单元结构体 │
├────────────────┼─────────────────┼─────────────────┼──────────────────┤
│ 字段名称 │ 有 │ 无 │ 无 │
│ 访问方式 │ .name │ .0 .1 .2 │ N/A │
│ 内存大小 │ 字段和+对齐 │ 字段和+对齐 │ 0 │
│ 主要用途 │ 数据建模 │ NewType/简单 │ 标记/trait │
└────────────────┴─────────────────┴─────────────────┴──────────────────┘
创建与初始化
基本创建与字段简写
struct Point {
x: i32,
y: i32,
}
fn main() {
// 基本创建(字段顺序可以不同)
let p1 = Point { x: 10, y: 20 };
let p2 = Point { y: 50, x: 30 };
// 字段简写:变量名与字段名相同时
let x = 100;
let y = 200;
let p3 = Point { x, y }; // 等同于 Point { x: x, y: y }
println!("p1: ({}, {})", p1.x, p1.y);
println!("p3: ({}, {})", p3.x, p3.y);
}
结构体更新语法
struct Config {
host: String,
port: u16,
timeout: u32,
debug: bool,
}
fn main() {
let config1 = Config {
host: String::from("localhost"),
port: 8080,
timeout: 30,
debug: true,
};
// 用 ..config1 复制剩余字段
let config2 = Config {
port: 3000, // 只修改 port
..config1 // 其余从 config1 复制
};
// ⚠️ 注意所有权!String 被移动了
// println!("{}", config1.host); // ❌ host 已移动
println!("{}", config1.port); // ✅ u16 是 Copy 类型
println!("{}", config1.debug); // ✅ bool 是 Copy 类型
println!("config2 port: {}", config2.port);
}
字段访问与修改
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
// 读取字段
let rect = Rectangle { width: 30, height: 50 };
println!("宽: {}, 高: {}", rect.width, rect.height);
let area = rect.width * rect.height;
println!("面积: {}", area);
// 修改字段 - 整个结构体必须是 mut
let mut rect2 = Rectangle { width: 10, height: 20 };
rect2.width = 100;
rect2.height = 200;
println!("修改后: {}x{}", rect2.width, rect2.height);
// 通过引用访问(自动解引用)
let rect_ref = ▭
println!("通过引用: {}", rect_ref.width);
// 通过可变引用修改
modify_rect(&mut rect2);
println!("函数修改后: {}x{}", rect2.width, rect2.height);
}
fn modify_rect(r: &mut Rectangle) {
r.width *= 2;
r.height *= 2;
}
所有权与借用
字段的移动
struct Container {
data: String,
count: u32,
}
fn main() {
let c = Container {
data: String::from("hello"),
count: 1,
};
// 移动单个字段
let data = c.data;
// println!("{}", c.data); // ❌ data 已移动
println!("{}", c.count); // ✅ u32 是 Copy,仍可用
println!("{}", data);
}
部分借用
struct Data {
field1: String,
field2: String,
}
fn main() {
let mut data = Data {
field1: String::from("one"),
field2: String::from("two"),
};
// 可以同时借用不同字段(Rust 能追踪)
let r1 = &data.field1; // 不可变借用 field1
let r2 = &mut data.field2; // 可变借用 field2
r2.push_str(" modified");
println!("{}, {}", r1, r2); // ✅ 借用不同字段,允许
}
内存布局
默认布局
struct Example {
a: u8, // 1 byte
b: u32, // 4 bytes
c: u8, // 1 byte
}
fn main() {
println!("大小: {} bytes", std::mem::size_of::<Example>()); // 8
println!("对齐: {} bytes", std::mem::align_of::<Example>()); // 4
}
Rust 编译器可能重排字段以优化内存:
┌────────────────────────────┬──────┬──────┬──────────────┐
│ b (u32) │ a │ c │ padding │
│ 4 bytes │ 1B │ 1B │ 2 bytes │
└────────────────────────────┴──────┴──────┴──────────────┘
总大小: 8 bytes(优化后)
repr 属性控制布局
struct Example {
a: u8, // 1 byte
b: u32, // 4 bytes
c: u8, // 1 byte
}
// C 语言布局(不重排,FFI 兼容)
#[repr(C)]
struct CLayout {
a: u8,
b: u32,
c: u8,
}
// 紧凑布局(无填充)
#[repr(packed)]
struct Packed {
a: u8,
b: u32,
c: u8,
}
fn main() {
println!("默认: {} bytes", std::mem::size_of::<Example>()); // 8
println!("repr(C): {} bytes", std::mem::size_of::<CLayout>()); // 12
println!("packed: {} bytes", std::mem::size_of::<Packed>()); // 6
}
打印结构体
// 派生 Debug trait 才能打印
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
println!("{:?}", p); // Point { x: 10, y: 20 }
println!("{:#?}", p); // 美化输出(多行)
}
枚举
枚举是 Rust 中定义"多选一"类型的方式,一个值在某一时刻只能是其中一个变体。
基本枚举定义
简单枚举(无关联数据)
// 定义枚举
enum Direction {
Up,
Down,
Left,
Right,
}
enum Status {
Pending,
Running,
Completed,
Failed,
}
fn main() {
// 创建枚举值
let dir = Direction::Up;
let status = Status::Running;
// 使用 match 匹配(枚举必须穷尽所有变体)
match dir {
Direction::Up => println!("向上"),
Direction::Down => println!("向下"),
Direction::Left => println!("向左"),
Direction::Right => println!("向右"),
}
// if let 匹配单个变体
if let Status::Running = status {
println!("正在运行中...");
}
}
带关联数据的枚举
// 每个变体可以携带不同类型、不同数量的数据
enum Message {
Quit, // 无数据(类似单元结构体)
Move { x: i32, y: i32 }, // 命名字段(类似结构体)
Write(String), // 单个值(类似元组结构体)
ChangeColor(u8, u8, u8), // 多个值(类似元组结构体)
}
fn main() {
// 创建不同变体
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Write(String::from("hello"));
let m4 = Message::ChangeColor(255, 128, 0);
// 匹配并提取数据
match m2 {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("消息: {}", text),
Message::ChangeColor(r, g, b) => println!("颜色: RGB({},{},{})", r, g, b),
}
}
三种变体形式
enum Example {
// 1. 单元变体(无数据)
Unit,
// 2. 元组变体(匿名字段)
Tuple(i32, String),
// 3. 结构体变体(命名字段)
Struct { id: u32, name: String },
}
fn main() {
let a = Example::Unit;
let b = Example::Tuple(42, String::from("hello"));
let c = Example::Struct { id: 1, name: String::from("test") };
// 匹配提取
match b {
Example::Unit => println!("单元变体"),
Example::Tuple(num, text) => println!("元组: {}, {}", num, text),
Example::Struct { id, name } => println!("结构体: {} - {}", id, name),
}
}
┌─────────────────┬────────────────────┬─────────────────────────────┐
│ 变体类型 │ 语法 │ 示例 │
├─────────────────┼────────────────────┼─────────────────────────────┤
│ 单元变体 │ Name │ Quit, None, Empty │
│ 元组变体 │ Name(T1, T2) │ Some(42), Move(10, 20) │
│ 结构体变体 │ Name { f: T } │ Point { x: 1, y: 2 } │
└─────────────────┴────────────────────┴─────────────────────────────┘
枚举的判别值
默认判别值
enum Number {
Zero, // 0
One, // 1
Two, // 2
}
fn main() {
// 转换为整数
println!("Zero = {}", Number::Zero as i32); // 0
println!("One = {}", Number::One as i32); // 1
println!("Two = {}", Number::Two as i32); // 2
}
自定义判别值
enum HttpStatus {
Ok = 200,
NotFound = 404,
InternalError = 500,
}
enum Color {
Red = 0xFF0000,
Green = 0x00FF00,
Blue = 0x0000FF,
}
// 指定底层类型
#[repr(u8)]
enum Flag {
A = 1,
B = 2,
C = 4,
}
fn main() {
println!("OK = {}", HttpStatus::Ok as i32); // 200
println!("NotFound = {}", HttpStatus::NotFound as i32); // 404
println!("Red = 0x{:06X}", Color::Red as i32); // 0xFF0000
println!("Flag 大小: {} byte", std::mem::size_of::<Flag>()); // 1
}
部分指定(自动递增)
enum Mixed {
A, // 0
B = 10, // 10
C, // 11(从上一个递增)
D, // 12
E = 100, // 100
F, // 101
}
fn main() {
println!("A={}, B={}, C={}, D={}, E={}, F={}",
Mixed::A as i32, Mixed::B as i32, Mixed::C as i32,
Mixed::D as i32, Mixed::E as i32, Mixed::F as i32);
// 输出: A=0, B=10, C=11, D=12, E=100, F=101
}
核心枚举
Option 和 Result 是 Rust 中最重要的两个枚举,用于处理空值和错误,替代了其他语言中的 null 和异常机制。
Option\<T> - 可选值
定义
// 标准库中的定义
enum Option<T> {
Some(T), // 有值,包含一个 T 类型的值
None, // 无值
}
为什么需要 Option?
// 其他语言中的问题:null/nil 导致的崩溃
// Java: String s = null; s.length(); → NullPointerException
// C: int* p = NULL; *p = 10; → 段错误
// Rust 没有 null!用 Option 明确表示"可能没有值"
fn main() {
// 明确告诉编译器:这个变量可能没有值
let maybe_number: Option<i32> = Some(42);
let no_number: Option<i32> = None;
// 编译器强制你处理 None 的情况,避免空指针错误
}
创建 Option
fn main() {
// 有值:Some(值)
let a: Option<i32> = Some(5);
let b = Some("hello"); // 自动推断 Option<&str>
let c = Some(3.14); // 自动推断 Option<f64>
// 无值:None(通常需要类型标注)
let d: Option<i32> = None;
let e: Option<String> = None;
// 打印
println!("a = {:?}", a); // Some(5)
println!("d = {:?}", d); // None
}
Option 不能直接使用内部值
fn main() {
let x: Option<i32> = Some(5);
let y: i32 = 10;
// ❌ 错误:Option<i32> 不是 i32,不能直接运算
// let sum = x + y;
// ✅ 必须先"解包"取出里面的值
let sum = x.unwrap() + y; // unwrap 取出 Some 里的值
println!("sum = {}", sum); // 15
}
基本方法
fn main() {
let some_val: Option<i32> = Some(10);
let none_val: Option<i32> = None;
// ===== 检查状态 =====
println!("some_val.is_some() = {}", some_val.is_some()); // true
println!("some_val.is_none() = {}", some_val.is_none()); // false
println!("none_val.is_some() = {}", none_val.is_some()); // false
println!("none_val.is_none() = {}", none_val.is_none()); // true
// ===== 获取值(危险方法,None 会 panic)=====
let v1 = some_val.unwrap(); // 返回 10
println!("unwrap = {}", v1);
// none_val.unwrap(); // ❌ panic!
let v2 = some_val.expect("错误信息"); // 同 unwrap,但可自定义错误信息
println!("expect = {}", v2);
// none_val.expect("值不存在"); // ❌ panic: "值不存在"
// ===== 安全获取值 =====
// unwrap_or: None 时返回指定默认值
println!("some.unwrap_or(0) = {}", some_val.unwrap_or(0)); // 10
println!("none.unwrap_or(0) = {}", none_val.unwrap_or(0)); // 0
// unwrap_or_default: None 时返回类型的默认值
let s: Option<String> = None;
println!("default = '{}'", s.unwrap_or_default()); // ""(空字符串)
let n: Option<i32> = None;
println!("default = {}", n.unwrap_or_default()); // 0
}
常见使用场景
fn main() {
// 场景1:查找可能不存在的元素
let numbers = vec![1, 2, 3, 4, 5];
let first: Option<&i32> = numbers.first(); // Some(&1)
let tenth: Option<&i32> = numbers.get(10); // None
println!("first = {:?}", first); // Some(1)
println!("tenth = {:?}", tenth); // None
// 场景2:字符串解析可能失败
let good: Result<i32, _> = "42".parse();
let bad: Result<i32, _> = "abc".parse();
// parse 返回 Result,可以用 .ok() 转为 Option
let good_opt: Option<i32> = "42".parse().ok(); // Some(42)
let bad_opt: Option<i32> = "abc".parse().ok(); // None
println!("good_opt = {:?}", good_opt);
println!("bad_opt = {:?}", bad_opt);
// 场景3:HashMap 查找
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("a", 1);
let val: Option<&i32> = map.get("a"); // Some(&1)
let none: Option<&i32> = map.get("b"); // None
}
Result\<T, E> - 错误处理
定义
// 标准库中的定义
enum Result<T, E> {
Ok(T), // 成功,包含成功值 T
Err(E), // 失败,包含错误值 E
}
为什么需要 Result?
// 其他语言的问题:异常可能被忽略或遗漏
// Java: 可能忘记 catch
// C: 可能忘记检查返回值
// Rust 的 Result 强制你处理错误
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("除数不能为零")) // 返回错误
} else {
Ok(a / b) // 返回成功结果
}
}
fn main() {
let result = divide(10, 2);
// 编译器强制你处理两种情况
// 不能直接使用 result,必须检查是 Ok 还是 Err
}
创建 Result
fn main() {
// 成功:Ok(值)
let success: Result<i32, String> = Ok(42);
let ok_str: Result<&str, i32> = Ok("成功");
// 失败:Err(错误)
let failure: Result<i32, String> = Err(String::from("失败了"));
let err_num: Result<&str, i32> = Err(404);
// 打印
println!("success = {:?}", success); // Ok(42)
println!("failure = {:?}", failure); // Err("失败了")
}
基本方法
fn main() {
let ok_val: Result<i32, &str> = Ok(10);
let err_val: Result<i32, &str> = Err("错误");
// ===== 检查状态 =====
println!("ok_val.is_ok() = {}", ok_val.is_ok()); // true
println!("ok_val.is_err() = {}", ok_val.is_err()); // false
println!("err_val.is_ok() = {}", err_val.is_ok()); // false
println!("err_val.is_err() = {}", err_val.is_err()); // true
// ===== 获取值(危险方法)=====
let v1 = ok_val.unwrap(); // 返回 10
println!("unwrap = {}", v1);
// err_val.unwrap(); // ❌ panic!
let v2 = ok_val.expect("失败了"); // 同 unwrap
println!("expect = {}", v2);
// 获取错误值
let e = err_val.unwrap_err(); // 返回 "错误"
println!("unwrap_err = {}", e);
// ok_val.unwrap_err(); // ❌ panic!
// ===== 安全获取值 =====
println!("ok.unwrap_or(0) = {}", ok_val.unwrap_or(0)); // 10
println!("err.unwrap_or(0) = {}", err_val.unwrap_or(0)); // 0
// ===== 转换为 Option =====
let opt1: Option<i32> = ok_val.ok(); // Some(10),丢弃错误
let opt2: Option<i32> = err_val.ok(); // None
let opt3: Option<&str> = err_val.err(); // Some("错误"),丢弃成功值
let opt4: Option<&str> = ok_val.err(); // None
println!("ok_val.ok() = {:?}", opt1); // Some(10)
println!("err_val.ok() = {:?}", opt2); // None
}
常见使用场景
use std::fs::File;
use std::io::Read;
fn main() {
// 场景1:文件操作
let file_result: Result<File, std::io::Error> = File::open("hello.txt");
// 必须处理可能的错误
let file = file_result.unwrap_or_else(|error| {
panic!("打开文件失败: {:?}", error);
});
// 场景2:字符串解析
let num: Result<i32, _> = "42".parse();
let bad: Result<i32, _> = "abc".parse();
println!("num = {:?}", num); // Ok(42)
println!("bad = {:?}", bad); // Err(ParseIntError)
// 场景3:自定义函数返回 Result
fn validate_age(age: i32) -> Result<i32, String> {
if age < 0 {
Err(String::from("年龄不能为负"))
} else if age > 150 {
Err(String::from("年龄不合理"))
} else {
Ok(age)
}
}
println!("{:?}", validate_age(25)); // Ok(25)
println!("{:?}", validate_age(-5)); // Err("年龄不能为负")
println!("{:?}", validate_age(200)); // Err("年龄不合理")
}
相互转换
fn main() {
// ===== Option → Result =====
let opt: Option<i32> = Some(5);
let none: Option<i32> = None;
// ok_or: 把 None 转换为指定的 Err
let res1: Result<i32, &str> = opt.ok_or("无值"); // Ok(5)
let res2: Result<i32, &str> = none.ok_or("无值"); // Err("无值")
println!("opt.ok_or() = {:?}", res1);
println!("none.ok_or() = {:?}", res2);
// ===== Result → Option =====
let ok: Result<i32, &str> = Ok(10);
let err: Result<i32, &str> = Err("错误");
// ok(): 丢弃错误,Err → None
let opt1: Option<i32> = ok.ok(); // Some(10)
let opt2: Option<i32> = err.ok(); // None
println!("ok.ok() = {:?}", opt1);
println!("err.ok() = {:?}", opt2);
// err(): 丢弃成功值,Ok → None
let opt3: Option<&str> = ok.err(); // None
let opt4: Option<&str> = err.err(); // Some("错误")
println!("ok.err() = {:?}", opt3);
println!("err.err() = {:?}", opt4);
}
内存布局
基本原理
enum Simple {
A,
B,
C,
}
enum WithData {
Empty,
Number(i32),
Text(String),
}
fn main() {
// 简单枚举:只需存储判别值
println!("Simple 大小: {} bytes", std::mem::size_of::<Simple>()); // 1
// 带数据枚举:判别值 + 最大变体的大小
println!("WithData 大小: {} bytes", std::mem::size_of::<WithData>());
println!("String 大小: {} bytes", std::mem::size_of::<String>()); // 24
}
枚举内存布局 = 判别值(tag)+ 数据(payload)
WithData 布局示意:
┌──────────┬─────────────────────────────────────────┐
│ tag │ payload │
│ (8B) │ (最大变体的大小) │
├──────────┼─────────────────────────────────────────┤
│ 0 │ (Empty: 无数据,但空间仍保留) │
│ 1 │ Number: i32 (4B) + padding │
│ 2 │ Text: String (24B) │
└──────────┴─────────────────────────────────────────┘
数组
数组是固定长度、存储在栈上的同类型元素集合,长度是类型的一部分。
基本定义与创建
fn main() {
// 1. 显式类型标注:[类型; 长度]
let arr1: [i32; 5] = [1, 2, 3, 4, 5];
// 2. 类型推断
let arr2 = [1, 2, 3, 4, 5]; // 推断为 [i32; 5]
let arr3 = [1.0, 2.0, 3.0]; // 推断为 [f64; 3]
let arr4 = ["hello", "world"]; // 推断为 [&str; 2]
// 3. 重复值初始化:[初始值; 长度]
let zeros = [0; 10]; // 10 个 0
let ones = [1u8; 100]; // 100 个 1u8
let spaces = [' '; 20]; // 20 个空格
// 4. 空数组
let empty: [i32; 0] = [];
println!("arr1: {:?}", arr1);
println!("zeros: {:?}", zeros);
println!("empty 长度: {}", empty.len());
}
长度是类型的一部分
fn main() {
let a: [i32; 3] = [1, 2, 3];
let b: [i32; 5] = [1, 2, 3, 4, 5];
// a 和 b 是不同类型!
// [i32; 3] ≠ [i32; 5]
println!("[i32; 3] 大小: {} bytes", std::mem::size_of::<[i32; 3]>()); // 12
println!("[i32; 5] 大小: {} bytes", std::mem::size_of::<[i32; 5]>()); // 20
// 函数参数必须指定具体长度
fn print_three(arr: [i32; 3]) {
println!("{:?}", arr);
}
print_three(a); // ✅
// print_three(b); // ❌ 类型不匹配
}
┌─────────────────────────────────────────────────────────┐
│ 数组类型 = 元素类型 + 长度 │
│ │
│ [i32; 3] → 3 个 i32,共 12 bytes │
│ [i32; 5] → 5 个 i32,共 20 bytes │
│ [u8; 100] → 100 个 u8,共 100 bytes │
│ │
│ 这三个是完全不同的类型! │
└─────────────────────────────────────────────────────────┘
元素访问
索引访问
fn main() {
let arr = [10, 20, 30, 40, 50];
// 读取元素(索引从 0 开始)
let first = arr[0]; // 10
let third = arr[2]; // 30
let last = arr[4]; // 50
println!("第一个: {}, 第三个: {}, 最后一个: {}", first, third, last);
// 修改元素(需要 mut)
let mut arr2 = [1, 2, 3];
arr2[0] = 100;
arr2[2] = 300;
println!("修改后: {:?}", arr2); // [100, 2, 300]
}
越界检查(运行时 panic)
fn main() {
let arr = [1, 2, 3, 4, 5];
// 编译时已知越界 → 编译警告/错误
// let x = arr[10]; // 编译器直接报错
// 运行时越界 → panic
let index = 10;
let x = arr[index]; // panic: index out of bounds
// error: this operation will panic at runtime
// --> ./main.rs:9:13
// |
// 9 | let x = arr[index]; // panic: index out of bounds
// | ^^^^^^^^^^ index out of bounds: the length is 5 but the index is 10
// |
// = note: `#[deny(unconditional_panic)]` on by default
// 安全访问:使用 get() 返回 Option
match arr.get(2) {
Some(value) => println!("索引 2 的值: {}", value),
None => println!("索引越界"),
}
match arr.get(10) {
Some(value) => println!("值: {}", value),
None => println!("索引 10 越界"), // 这个会执行
}
// 简洁写法
if let Some(v) = arr.get(3) {
println!("值: {}", v);
}
}
遍历数组
fn main() {
let arr = [10, 20, 30, 40, 50];
// 方式 1:for 循环(借用元素)
println!("遍历引用:");
for item in &arr {
println!(" {}", item);
}
// 方式 2:for 循环(获取所有权,Copy 类型会复制)
println!("遍历值:");
for item in arr {
println!(" {}", item);
}
// 方式 3:带索引遍历
println!("带索引:");
for (index, value) in arr.iter().enumerate() {
println!(" arr[{}] = {}", index, value);
}
// 方式 4:索引遍历
println!("索引遍历:");
for i in 0..arr.len() {
println!(" arr[{}] = {}", i, arr[i]);
}
// 方式 5:可变遍历
let mut arr2 = [1, 2, 3];
for item in &mut arr2 {
*item *= 2;
}
println!("翻倍后: {:?}", arr2); // [2, 4, 6]
}
常用方法
fn main() {
let arr = [3, 1, 4, 1, 5, 9, 2, 6];
// 基本信息
println!("长度: {}", arr.len()); // 8
println!("是否为空: {}", arr.is_empty()); // false
// 获取元素
println!("第一个: {:?}", arr.first()); // Some(3)
println!("最后一个: {:?}", arr.last()); // Some(6)
println!("get(2): {:?}", arr.get(2)); // Some(4)
println!("get(100): {:?}", arr.get(100)); // None
// 切片操作
let slice = &arr[2..5]; // [4, 1, 5]
println!("切片 [2..5]: {:?}", slice);
// 包含检查
println!("包含 5: {}", arr.contains(&5)); // true
println!("包含 7: {}", arr.contains(&7)); // false
// 迭代器
let sum: i32 = arr.iter().sum();
println!("总和: {}", sum); // 31
let max = arr.iter().max();
println!("最大值: {:?}", max); // Some(9)
// 查找
let pos = arr.iter().position(|&x| x == 5);
println!("5 的位置: {:?}", pos); // Some(4)
}
排序与修改
fn main() {
let mut arr = [3, 1, 4, 1, 5, 9, 2, 6];
// 排序
arr.sort();
println!("升序: {:?}", arr); // [1, 1, 2, 3, 4, 5, 6, 9]
arr.sort_by(|a, b| b.cmp(a));
println!("降序: {:?}", arr); // [9, 6, 5, 4, 3, 2, 1, 1]
// 反转
arr.reverse();
println!("反转: {:?}", arr); // [1, 1, 2, 3, 4, 5, 6, 9]
// 填充
let mut arr2 = [0; 5];
arr2.fill(42);
println!("填充: {:?}", arr2); // [42, 42, 42, 42, 42]
// 交换元素
let mut arr3 = [1, 2, 3, 4, 5];
arr3.swap(0, 4);
println!("交换后: {:?}", arr3); // [5, 2, 3, 4, 1]
// 旋转
let mut arr4 = [1, 2, 3, 4, 5];
arr4.rotate_left(2);
println!("左旋 2: {:?}", arr4); // [3, 4, 5, 1, 2]
}
数组与切片
fn main() {
let arr = [1, 2, 3, 4, 5];
// 数组可以隐式转换为切片引用
let slice: &[i32] = &arr;
// 部分切片
let part: &[i32] = &arr[1..4]; // [2, 3, 4]
println!("部分切片: {:?}", part);
// 切片的优势:函数可以接受任意长度
fn sum_slice(data: &[i32]) -> i32 {
data.iter().sum()
}
let arr3 = [1, 2, 3];
let arr5 = [1, 2, 3, 4, 5];
// 不同长度的数组都能传入
println!("sum arr3: {}", sum_slice(&arr3)); // 6
println!("sum arr5: {}", sum_slice(&arr5)); // 15
println!("sum part: {}", sum_slice(&arr5[2..])); // 12
}
┌─────────────────────────────────────────────────────────┐
│ 数组 [T; N] vs 切片 &[T] │
├─────────────────────────────────────────────────────────┤
│ 长度编译时固定 长度运行时确定 │
│ 存储在栈上 是引用(胖指针) │
│ 大小 = N * sizeof(T) 大小 = 2 * 指针 │
│ 类型包含长度 类型不包含长度 │
│ [i32; 3] ≠ [i32; 5] &[i32] 通用 │
└─────────────────────────────────────────────────────────┘
多维数组
fn main() {
// 二维数组:3 行 4 列
let matrix: [[i32; 4]; 3] = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
];
// 访问元素
println!("matrix[1][2] = {}", matrix[1][2]); // 7
// 遍历二维数组
for row in &matrix {
for col in row {
print!("{:3} ", col);
}
println!();
}
// 初始化为零
let zeros: [[i32; 4]; 3] = [[0; 4]; 3];
println!("零矩阵: {:?}", zeros);
// 三维数组
let cube: [[[i32; 2]; 3]; 4] = [[[0; 2]; 3]; 4];
println!("三维数组大小: {} bytes", std::mem::size_of_val(&cube)); // 96
}
内存布局
fn main() {
let arr = [1i32, 2, 3, 4, 5];
// 连续内存存储
println!("数组地址: {:p}", &arr);
println!("arr[0] 地址: {:p}", &arr[0]);
println!("arr[1] 地址: {:p}", &arr[1]);
println!("arr[2] 地址: {:p}", &arr[2]);
// 每个元素间隔 4 bytes (i32 大小)
// 数组大小 = 元素大小 × 长度
println!("i32 大小: {} bytes", std::mem::size_of::<i32>());
println!("数组大小: {} bytes", std::mem::size_of_val(&arr)); // 20
}
内存布局(栈上连续存储):
arr: [1i32, 2, 3, 4, 5]
地址: 0x1000 0x1004 0x1008 0x100C 0x1010
┌────────┬────────┬────────┬────────┬────────┐
│ 1 │ 2 │ 3 │ 4 │ 5 │
│ (i32) │ (i32) │ (i32) │ (i32) │ (i32) │
└────────┴────────┴────────┴────────┴────────┘
│◄──────────────── 20 bytes ───────────────►│
流程控制
┌─────────────────────────────────────────────────────────────────────────┐
│ Rust 流程控制 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 条件判断 循环 模式匹配 │
│ ├── if/else ├── loop ├── match │
│ ├── if let ├── while ├── if let / while let │
│ ├── let else ├── while let ├── @ 绑定 │
│ └── matches! └── for └── 模式语法 │
│ │
│ 跳转控制 错误处理 表达式 │
│ ├── break ├── ? 运算符 └── 块表达式 │
│ ├── continue ├── panic! │
│ ├── return └── unwrap/expect │
│ └── 'label 标签 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
条件判断
if/else 表达式
Rust 中 if 是表达式而非语句,可以返回值。条件必须是 bool 类型,不会自动转换。
fn main() {
let number = 42;
// 基本用法
if number > 0 {
println!("正数");
} else if number < 0 {
println!("负数");
} else {
println!("零");
}
// 作为表达式返回值(各分支类型必须一致)
let description = if number % 2 == 0 { "偶数" } else { "奇数" };
println!("description: {}", description);
// 在 let 语句中使用
let abs_value = if number >= 0 { number } else { -number };
println!("abs_value: {}", abs_value);
// 嵌套条件
let category = if number > 100 {
"大"
} else {
if number > 10 { "中" } else { "小" }
};
println!("category: {}", category);
}
if let 表达式
当只关心一种匹配情况时,if let 比 match 更简洁。
fn main() {
let some_value: Option<i32> = Some(42);
// 替代冗长的 match
if let Some(x) = some_value {
println!("值为: {}", x);
} else {
println!("无值");
}
// 等价的 match 写法
match some_value {
Some(x) => println!("值为: {}", x),
_ => println!("无值"),
}
// 可以结合 else if / else if let
let config: Result<i32, &str> = Ok(100);
if let Ok(value) = config {
println!("配置值: {}", value);
} else if let Err(e) = config {
println!("配置错误: {}", e);
}
// 解构复杂类型
struct Point { x: i32, y: i32 }
let point = Some(Point { x: 10, y: 20 });
if let Some(Point { x, y }) = point {
println!("坐标: ({}, {})", x, y);
}
}
let else 表达式
Rust 1.65 引入,用于匹配失败时必须发散(提前退出)的场景。
// 必须以 return/break/continue/panic 等发散表达式结尾
fn get_username(id: Option<u32>) -> String {
let Some(user_id) = id else {
return String::from("anonymous");
};
format!("user_{}", user_id)
}
// 在循环中使用 break
fn find_first_even(numbers: &[i32]) -> Option<i32> {
for &n in numbers {
let 0 = n % 2 else {
continue; // 不是偶数,跳过
};
return Some(n);
}
None
}
// 多层解构
fn process_data(data: Option<Result<String, String>>) -> String {
let Some(result) = data else {
return String::new();
};
let Ok(content) = result else {
return String::from("error");
};
content
}
fn main() {
let username = get_username(Some(123));
println!("username: {}", username);
let even = find_first_even(&[1, 2, 3, 4, 5]);
println!("even: {:?}", even);
let data = process_data(Some(Ok(String::from("Hello, world!"))));
println!("data: {}", data);
let data = process_data(Some(Err(String::from("Error"))));
println!("data: {}", data);
let data = process_data(None);
println!("data: {}", data);
}
matches! 宏
返回 bool 的模式匹配,适合在条件判断中使用。
fn main() {
let c = 'f';
// 检查是否匹配模式
let is_lowercase = matches!(c, 'a'..='z');
println!("is_lowercase: {}", is_lowercase);
let is_letter = matches!(c, 'a'..='z' | 'A'..='Z');
println!("is_letter: {}", is_letter);
let is_hex = matches!(c, '0'..='9' | 'a'..='f' | 'A'..='F');
println!("is_hex: {}", is_hex);
// 带守卫条件
let opt = Some(42);
let is_positive = matches!(opt, Some(x) if x > 0);
let is_large = matches!(opt, Some(x) if x > 100);
println!("is_positive: {}", is_positive);
println!("is_large: {}", is_large);
// 匹配枚举变体
enum Status { Active, Inactive, Pending(u32) }
let status = Status::Pending(5);
let is_pending = matches!(status, Status::Pending(_));
let is_urgent = matches!(status, Status::Pending(x) if x < 10);
println!("is_pending: {}", is_pending);
println!("is_urgent: {}", is_urgent);
// 在 filter 中使用
let numbers = vec![Some(1), None, Some(3), None, Some(5)];
let valid: Vec<_> = numbers.iter()
.filter(|x| matches!(x, Some(_)))
.collect();
println!("valid: {:?}", valid);
}
循环控制
loop 无限循环
loop 创建无限循环,必须通过 break 退出,可以返回值。
// 基本无限循环
let mut count = 0;
loop {
count += 1;
if count == 10 {
break;
}
}
// 通过 break 返回值
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 返回 20
}
};
// 重试逻辑
let mut attempts = 0;
let connection = loop {
attempts += 1;
match try_connect() {
Ok(conn) => break conn,
Err(e) if attempts < 3 => {
println!("重试 {}/3...", attempts);
continue;
}
Err(e) => panic!("连接失败: {}", e),
}
};
// 状态机
enum State { Start, Running, Finished }
let mut state = State::Start;
loop {
state = match state {
State::Start => {
println!("启动");
State::Running
}
State::Running => {
println!("运行中");
State::Finished
}
State::Finished => {
println!("完成");
break;
}
};
}
while 条件循环
条件为真时持续执行。
// 基本用法
let mut n = 0;
while n < 5 {
println!("{}", n);
n += 1;
}
// 读取输入直到特定条件
let mut input = String::new();
while input.trim() != "quit" {
input.clear();
std::io::stdin().read_line(&mut input).unwrap();
println!("你输入了: {}", input.trim());
}
// 处理可变状态
let mut stack = vec![1, 2, 3, 4, 5];
while !stack.is_empty() {
let top = stack.pop().unwrap();
println!("弹出: {}", top);
}
while let 模式匹配循环
结合模式匹配的条件循环,常用于迭代器和 Option/Result。
// 遍历 Option
let mut optional = Some(0);
while let Some(i) = optional {
if i > 5 {
optional = None;
} else {
println!("{}", i);
optional = Some(i + 1);
}
}
// 消费栈
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("处理: {}", top);
}
// 通道接收
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
// 在另一个线程中发送数据...
while let Ok(msg) = rx.recv() {
println!("收到: {}", msg);
}
// 解构复杂类型
let mut pairs = vec![(1, 'a'), (2, 'b'), (3, 'c')];
while let Some((num, ch)) = pairs.pop() {
println!("{}: {}", num, ch);
}
for 迭代循环
Rust 最常用的循环,遍历任何实现了 IntoIterator 的类型。
// 范围迭代
for i in 0..5 {
println!("{}", i); // 0, 1, 2, 3, 4
}
for i in 0..=5 {
println!("{}", i); // 0, 1, 2, 3, 4, 5(包含结尾)
}
// 反向迭代
for i in (0..5).rev() {
println!("{}", i); // 4, 3, 2, 1, 0
}
// 遍历集合(三种方式)
let v = vec![1, 2, 3];
for item in &v { // 借用,item: &i32
println!("{}", item);
}
for item in &mut v { // 可变借用,item: &mut i32
*item += 1;
}
for item in v { // 获取所有权,item: i32
println!("{}", item);
}
// 带索引遍历
let colors = vec!["红", "绿", "蓝"];
for (index, color) in colors.iter().enumerate() {
println!("{}: {}", index, color);
}
// 遍历 HashMap
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert("Alice", 100);
scores.insert("Bob", 85);
for (name, score) in &scores {
println!("{}: {}", name, score);
}
// 步进迭代
for i in (0..10).step_by(2) {
println!("{}", i); // 0, 2, 4, 6, 8
}
// 并行迭代(使用 zip)
let names = vec!["Alice", "Bob"];
let ages = vec![25, 30];
for (name, age) in names.iter().zip(ages.iter()) {
println!("{} is {} years old", name, age);
}
跳转控制
break 退出循环
退出当前循环,可携带返回值,可指定标签跳出多层循环。
// 基本 break
for i in 0..10 {
if i == 5 {
break;
}
println!("{}", i);
}
// break 携带值(仅限 loop)
let result = loop {
let value = compute();
if value > 100 {
break value;
}
};
// 带标签的 break(跳出外层循环)
'outer: for i in 0..5 {
for j in 0..5 {
if i * j > 10 {
println!("在 ({}, {}) 处退出", i, j);
break 'outer;
}
}
}
// 标签 + 返回值
let result = 'search: loop {
for row in 0..10 {
for col in 0..10 {
if matrix[row][col] == target {
break 'search (row, col);
}
}
}
break 'search (-1, -1); // 未找到
};
continue 跳过迭代
跳过当前迭代的剩余部分,进入下一次迭代。
// 基本 continue
for i in 0..10 {
if i % 2 == 0 {
continue; // 跳过偶数
}
println!("{}", i); // 只打印奇数
}
// 带标签的 continue
'outer: for i in 0..5 {
for j in 0..5 {
if j == 2 {
continue 'outer; // 跳到外层下一次迭代
}
println!("({}, {})", i, j);
}
}
// 过滤处理
let items = vec![Some(1), None, Some(2), None, Some(3)];
for item in items {
let Some(value) = item else {
continue; // 跳过 None
};
println!("处理: {}", value);
}
return 函数返回
从函数中提前返回,可携带返回值。
// 提前返回
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
return None; // 提前返回
}
Some(a / b)
}
// 在嵌套结构中返回
fn find_in_matrix(matrix: &[Vec<i32>], target: i32) -> Option<(usize, usize)> {
for (i, row) in matrix.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
if value == target {
return Some((i, j)); // 找到后立即返回
}
}
}
None
}
// 隐式返回(最后一个表达式,不加分号)
fn square(x: i32) -> i32 {
x * x // 等价于 return x * x;
}
// 在闭包中使用(注意:return 只返回闭包,不返回外层函数)
fn process(numbers: Vec<i32>) -> Vec<i32> {
numbers.into_iter()
.filter_map(|n| {
if n < 0 { return None; } // 返回闭包
Some(n * 2)
})
.collect()
}
标签块表达式
从任意代码块中返回值,不仅限于循环。
// 基本标签块
let result = 'block: {
if condition_a {
break 'block "A";
}
if condition_b {
break 'block "B";
}
"default"
};
// 替代复杂的嵌套 if
fn categorize(value: i32) -> &'static str {
'check: {
if value < 0 {
break 'check "negative";
}
if value == 0 {
break 'check "zero";
}
if value < 10 {
break 'check "small";
}
if value < 100 {
break 'check "medium";
}
"large"
}
}
// 提前退出复杂逻辑
let validated = 'validation: {
let Some(name) = get_name() else { break 'validation false };
let Some(age) = get_age() else { break 'validation false };
if age < 18 { break 'validation false };
true
};
模式匹配
match 表达式
Rust 最强大的控制流结构,必须穷尽所有可能。
let number = 13;
// 基本匹配
match number {
1 => println!("一"),
2 => println!("二"),
3 => println!("三"),
_ => println!("其他"), // 通配符,匹配剩余所有
}
// 或模式
match number {
1 | 2 | 3 => println!("小数字"),
4..=10 => println!("中等数字"), // 范围模式(包含边界)
_ => println!("大数字"),
}
// 守卫条件
match number {
n if n < 0 => println!("负数: {}", n),
n if n == 0 => println!("零"),
n if n % 2 == 0 => println!("正偶数: {}", n),
n => println!("正奇数: {}", n),
}
// 匹配枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
let msg = Message::ChangeColor(255, 128, 0);
match msg {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("写入: {}", text),
Message::ChangeColor(r, g, b) => println!("颜色: RGB({}, {}, {})", r, g, b),
}
// 匹配返回值
let description = match number {
0 => "零",
1..=9 => "个位数",
10..=99 => "两位数",
_ => "更大的数",
};
@ 绑定
在匹配的同时将值绑定到变量。
let num = 5;
// 基本 @ 绑定
match num {
n @ 1..=5 => println!("1-5 范围内: {}", n),
n @ 6..=10 => println!("6-10 范围内: {}", n),
n => println!("范围外: {}", n),
}
// 或模式中的绑定
match num {
n @ (1 | 3 | 5 | 7 | 9) => println!("奇数: {}", n),
n @ (2 | 4 | 6 | 8 | 10) => println!("偶数: {}", n),
n => println!("其他: {}", n),
}
// 枚举中的 @ 绑定
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello { id: id_var @ 3..=7 } => {
println!("id 在 3-7 范围内: {}", id_var);
}
Message::Hello { id: 10..=12 } => {
println!("id 在 10-12 范围内");
}
Message::Hello { id } => {
println!("其他 id: {}", id);
}
}
// 结合守卫
match Some(42) {
Some(n @ 1..=100) if n % 2 == 0 => println!("1-100 的偶数: {}", n),
Some(n) => println!("其他: {}", n),
None => println!("无值"),
}
模式语法详解
// 解构元组
let point = (3, 5);
match point {
(0, 0) => println!("原点"),
(x, 0) => println!("x轴上, x = {}", x),
(0, y) => println!("y轴上, y = {}", y),
(x, y) => println!("点 ({}, {})", x, y),
}
// 解构结构体
struct Point { x: i32, y: i32 }
let p = Point { x: 0, y: 7 };
match p {
Point { x: 0, y } => println!("y轴上, y = {}", y),
Point { x, y: 0 } => println!("x轴上, x = {}", x),
Point { x, y } => println!("点 ({}, {})", x, y),
}
// .. 忽略剩余字段/元素
struct Point3D { x: i32, y: i32, z: i32 }
let p = Point3D { x: 1, y: 2, z: 3 };
let Point3D { x, .. } = p; // 只取 x,忽略 y 和 z
// 切片模式
let arr = [1, 2, 3, 4, 5];
match &arr[..] {
[] => println!("空"),
[single] => println!("单元素: {}", single),
[first, second] => println!("两个元素: {}, {}", first, second),
[first, .., last] => println!("首: {}, 尾: {}", first, last),
}
// @ 绑定切片剩余部分
let numbers = [1, 2, 3, 4, 5];
match numbers {
[first, rest @ ..] => {
println!("首: {}, 剩余: {:?}", first, rest);
}
}
// ref 和 ref mut - 创建引用
let mut pair = (String::from("hello"), 42);
match pair {
(ref s, ref mut n) => {
println!("字符串: {}", s); // s: &String
*n += 1; // n: &mut i32
}
}
// 嵌套解构
enum Color {
Rgb(u8, u8, u8),
Hsv(u8, u8, u8),
}
enum Message {
ChangeColor(Color),
Quit,
}
let msg = Message::ChangeColor(Color::Rgb(255, 128, 0));
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println!("RGB: ({}, {}, {})", r, g, b);
}
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println!("HSV: ({}, {}, {})", h, s, v);
}
Message::Quit => println!("退出"),
}
错误处理
? 运算符
简化错误传播,遇到 Err 或 None 时提前返回。
use std::fs::File;
use std::io::{self, Read};
// 传播 Result 错误
fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path)?; // 失败则返回 Err
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// 链式调用
fn read_file_compact(path: &str) -> Result<String, io::Error> {
let mut content = String::new();
File::open(path)?.read_to_string(&mut content)?;
Ok(content)
}
// 传播 Option 的 None
fn first_char(s: &str) -> Option<char> {
s.lines().next()?.chars().next()
}
// 在 main 中使用
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = read_file("config.txt")?;
println!("{}", content);
Ok(())
}
// 转换错误类型(配合 From trait)
fn process() -> Result<(), MyError> {
let content = std::fs::read_to_string("file.txt")?; // io::Error -> MyError
let data: Data = serde_json::from_str(&content)?; // serde::Error -> MyError
Ok(())
}
panic! 宏
不可恢复错误,终止程序(或在 panic=abort 模式下直接中止)。
// 基本用法
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("除数不能为零!");
}
a / b
}
// 格式化消息
fn get_item(index: usize, items: &[&str]) {
if index >= items.len() {
panic!("索引 {} 越界,数组长度为 {}", index, items.len());
}
// ...
}
// 标记不可达代码
fn process_type(t: &str) -> i32 {
match t {
"a" => 1,
"b" => 2,
"c" => 3,
_ => unreachable!("未知类型: {}", t), // 不应该到达这里
}
}
// 标记待实现
fn complex_algorithm() {
todo!("待实现复杂算法"); // 会 panic 并显示文件位置
}
fn placeholder() {
unimplemented!("此功能尚未实现");
}
unwrap 和 expect
快速提取值的方法,失败时 panic。
// unwrap - 失败时 panic,无自定义消息
let value: Option<i32> = Some(42);
let v = value.unwrap(); // 42
let none: Option<i32> = None;
// let v = none.unwrap(); // panic: called `Option::unwrap()` on a `None` value
// expect - 失败时 panic,带自定义消息
let config = std::fs::read_to_string("config.txt")
.expect("无法读取配置文件");
// 适用场景:确信不会失败
let num: i32 = "42".parse().expect("硬编码的字符串应该能解析");
// 安全替代方案
let value = some_option.unwrap_or(0); // 默认值
let value = some_option.unwrap_or_else(|| compute_default()); // 惰性默认值
let value = some_option.unwrap_or_default(); // 类型的 Default 值
// Result 的变体
let ok_value = result.unwrap_or(default);
let ok_value = result.unwrap_or_else(|e| handle_error(e));
let err_value = result.unwrap_err(); // 期望是 Err,提取错误
块表达式
Rust 中几乎所有代码块都是表达式,最后一行无分号即为返回值。
// 基本块表达式
let result = {
let a = 1;
let b = 2;
a + b // 无分号,返回 3
};
// 与加分号的区别
let unit = {
let a = 1;
let b = 2;
a + b; // 加分号,返回 ()
};
// 限制变量作用域
let message = {
let mut temp = String::new();
temp.push_str("Hello, ");
temp.push_str("World!");
temp
}; // temp 在此处被释放,message 获得所有权
// 复杂初始化
let connection = {
let config = load_config();
let pool = create_pool(&config);
let conn = pool.get_connection();
conn.configure(&config);
conn
};
// 在 match 分支中
let description = match status_code {
200 => "成功",
404 => {
log_not_found();
"未找到"
}
500..=599 => {
let msg = format!("服务器错误: {}", status_code);
log_error(&msg);
"服务器错误"
}
_ => "未知状态",
};
// unsafe 块也是表达式
let ptr_value = unsafe {
let ptr = some_raw_ptr;
*ptr
};
方法 Method
核心概念
方法是定义在结构体、枚举或 trait 对象上下文中的函数,第一个参数始终是 self,代表调用该方法的实例。
┌─────────────────────────────────────────────────────┐
│ 方法 vs 函数 │
├─────────────────────────────────────────────────────┤
│ 函数 (fn) │ 独立存在,不依附于任何类型 │
│ 方法 (method) │ 定义在 impl 块中,绑定到特定类型 │
└─────────────────────────────────────────────────────┘
基本语法
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 👇 方法:第一个参数是 self
fn area(&self) -> u32 {
self.width * self.height
}
// 👇 关联函数:没有 self 参数(类似其他语言的静态方法)
fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
}
fn main() {
let rect = Rectangle::new(30, 50); // 关联函数调用:::
println!("面积: {}", rect.area()); // 方法调用:.
}
self 的三种形式
| 形式 | 含义 | 所有权 | 使用场景 |
|---|---|---|---|
&self |
不可变借用 | 借用 | 只读访问(最常用) |
&mut self |
可变借用 | 借用 | 需要修改实例 |
self |
获取所有权 | 转移 | 转换类型或消费实例 |
impl Rectangle {
// 不可变借用:只读
fn area(&self) -> u32 {
self.width * self.height
}
// 可变借用:可修改
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
// 获取所有权:消费自身,返回新类型
fn to_square(self) -> Rectangle {
let side = self.width.max(self.height);
Rectangle { width: side, height: side }
}
}
自动引用和解引用
Rust 会自动添加 &、&mut 或 *,使方法调用更简洁:
let rect = Rectangle::new(10, 20);
// 以下两种写法等价:
rect.area();
(&rect).area(); // Rust 自动推导
多个 impl 块
一个类型可以有多个 impl 块(常用于 trait 实现分离):
impl Rectangle {
fn area(&self) -> u32 { /*...*/ }
}
impl Rectangle {
fn perimeter(&self) -> u32 { /*...*/ }
}
// 实现 trait
impl std::fmt::Display for Rectangle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}x{}", self.width, self.height)
}
}
枚举上的方法
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(s) => println!("消息: {}", s),
}
}
}
方法链式调用
impl Rectangle {
fn set_width(mut self, width: u32) -> Self {
self.width = width;
self // 返回自身,支持链式调用
}
fn set_height(mut self, height: u32) -> Self {
self.height = height;
self
}
}
// 链式调用
let rect = Rectangle::new(0, 0)
.set_width(100)
.set_height(50);
泛型 Generics
核心概念
泛型是一种参数化类型的机制,让代码可以处理多种数据类型,而无需重复编写。
┌─────────────────────────────────────────────────────────────┐
│ 泛型的价值 │
├─────────────────────────────────────────────────────────────┤
│ ✗ 没有泛型:fn max_i32(a: i32, b: i32) -> i32 │
│ fn max_f64(a: f64, b: f64) -> f64 重复代码! │
├─────────────────────────────────────────────────────────────┤
│ ✓ 使用泛型:fn max<T>(a: T, b: T) -> T 一份代码! │
└─────────────────────────────────────────────────────────────┘
函数泛型
// T 是类型参数,可以是任意名称(惯例用大写字母)
fn largest<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
fn main() {
println!("{}", largest(5, 10)); // T = i32
println!("{}", largest(3.14, 2.71)); // T = f64
println!("{}", largest('a', 'z')); // T = char
}
结构体泛型
// 单个类型参数
struct Point<T> {
x: T,
y: T,
}
// 多个类型参数
struct Pair<T, U> {
first: T,
second: U,
}
fn main() {
let int_point = Point { x: 5, y: 10 }; // Point<i32>
let float_point = Point { x: 1.0, y: 4.0 }; // Point<f64>
let mixed = Pair { first: 5, second: "hello" }; // Pair<i32, &str>
}
枚举泛型
标准库中最经典的两个泛型枚举:
// Option<T> - 表示可能存在的值
enum Option<T> {
Some(T),
None,
}
// Result<T, E> - 表示可能失败的操作
enum Result<T, E> {
Ok(T),
Err(E),
}
fn main() {
let number: Option<i32> = Some(42);
let text: Option<&str> = None;
let success: Result<i32, String> = Ok(200);
let failure: Result<i32, String> = Err("错误".to_string());
}
方法泛型
struct Point<T> {
x: T,
y: T,
}
// 为所有 Point<T> 实现方法
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Point { x, y }
}
fn x(&self) -> &T {
&self.x
}
}
// 仅为 Point<f64> 实现特定方法
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// 方法自身也可以有额外的泛型参数
impl<T> Point<T> {
fn mixup<U>(self, other: Point<U>) -> Point<T, U> {
Point {
x: self.x,
y: other.y,
}
}
}
Trait Bounds(特征约束)
基本语法
// 语法1:冒号约束
fn print_info<T: std::fmt::Display>(item: T) {
println!("{}", item);
}
// 语法2:where 子句(更清晰)
fn complex_function<T, U>(t: T, u: U) -> i32
where
T: Display + Clone,
U: Debug + PartialOrd,
{
// ...
}
常见约束模式
┌──────────────────┬────────────────────────────────────────┐
│ 约束形式 │ 含义 │
├──────────────────┼────────────────────────────────────────┤
│ T: Clone │ T 必须实现 Clone │
│ T: Clone + Debug │ T 必须同时实现 Clone 和 Debug │
│ T: 'static │ T 不包含非静态引用 │
│ T: ?Sized │ T 可以是动态大小类型 │
│ T: Default │ T 可以有默认值 │
└──────────────────┴────────────────────────────────────────┘
实际示例
use std::fmt::{Display, Debug};
// 要比较大小,需要 PartialOrd
// 要返回值,需要 Copy(或用引用)
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
// 使用 where 子句
fn notify<T>(item: &T)
where
T: Display + Clone
{
println!("通知: {}", item);
}
impl Trait 语法糖
// 参数位置:接受任何实现了 Display 的类型
fn print(item: impl Display) {
println!("{}", item);
}
// 返回位置:返回某个实现了 Iterator 的类型
fn counter() -> impl Iterator<Item = i32> {
(0..10).filter(|x| x % 2 == 0)
}
const 泛型(常量泛型)
// N 是常量泛型参数
struct Array<T, const N: usize> {
data: [T; N],
}
impl<T, const N: usize> Array<T, N> {
fn len(&self) -> usize {
N
}
}
fn main() {
let arr: Array<i32, 5> = Array { data: [1, 2, 3, 4, 5] };
println!("长度: {}", arr.len()); // 5
}
默认类型参数
// 标准库 Add trait 的定义
trait Add<Rhs = Self> { // Rhs 默认为 Self
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
// 使用默认类型
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
// ...
}
}
单态化(零成本抽象)
┌─────────────────────────────────────────────────────────────┐
│ 编译时单态化 │
├─────────────────────────────────────────────────────────────┤
│ 源代码: │
│ fn id<T>(x: T) -> T { x } │
│ id(5); // 调用1 │
│ id(3.14); // 调用2 │
├─────────────────────────────────────────────────────────────┤
│ 编译后生成: │
│ fn id_i32(x: i32) -> i32 { x } │
│ fn id_f64(x: f64) -> f64 { x } │
├─────────────────────────────────────────────────────────────┤
│ ✓ 运行时零开销 │
│ ✗ 可能增加二进制体积 │
└─────────────────────────────────────────────────────────────┘
常见使用模式
// 1. 构造函数模式
impl<T> Vec<T> {
pub fn new() -> Vec<T> { /* ... */ }
}
// 2. 转换模式
fn convert<T: Into<String>>(value: T) -> String {
value.into()
}
// 3. 条件实现
impl<T: Display> ToString for T {
// 所有实现 Display 的类型自动获得 ToString
}
// 4. 关联类型 vs 泛型参数
trait Iterator {
type Item; // 关联类型:每个实现只有一种 Item
fn next(&mut self) -> Option<Self::Item>;
}
trait Container<T> { // 泛型参数:可为同一类型多次实现
fn contains(&self, item: &T) -> bool;
}
特征 Trait
核心概念
Trait 定义了类型应该具备的行为(方法集合),类似于其他语言的接口(Interface),但更强大。
┌─────────────────────────────────────────────────────────────┐
│ Trait 的本质 │
├─────────────────────────────────────────────────────────────┤
│ • 定义共享行为的抽象 │
│ • 实现多态(编译时/运行时) │
│ • 约束泛型参数 │
│ • 扩展现有类型的能力 │
└─────────────────────────────────────────────────────────────┘
基本语法
定义 Trait
// 定义 trait
trait Summary {
fn summarize(&self) -> String; // 必须实现的方法
}
实现 Trait
struct Article {
title: String,
author: String,
content: String,
}
struct Tweet {
username: String,
content: String,
}
// 为 Article 实现 Summary
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
// 为 Tweet 实现 Summary
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("@{}: {}", self.username, self.content)
}
}
fn main() {
let article = Article {
title: String::from("Rust入门"),
author: String::from("张三"),
content: String::from("..."),
};
println!("{}", article.summarize()); // "Rust入门 by 张三"
}
默认实现
trait Summary {
// 默认实现
fn summarize(&self) -> String {
String::from("(阅读更多...)")
}
// 带默认实现的方法可以调用其他方法
fn summarize_author(&self) -> String;
fn full_summary(&self) -> String {
format!("作者: {} - {}", self.summarize_author(), self.summarize())
}
}
impl Summary for Article {
// 使用默认的 summarize()
fn summarize_author(&self) -> String {
self.author.clone()
}
}
impl Summary for Tweet {
// 覆盖默认实现
fn summarize(&self) -> String {
format!("@{}: {}", self.username, self.content)
}
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
Trait 作为参数
三种等价写法
// 1. impl Trait 语法(最简洁)
fn notify(item: &impl Summary) {
println!("速报: {}", item.summarize());
}
// 2. Trait Bound 语法
fn notify<T: Summary>(item: &T) {
println!("速报: {}", item.summarize());
}
// 3. where 子句(复杂约束时更清晰)
fn notify<T>(item: &T)
where
T: Summary
{
println!("速报: {}", item.summarize());
}
多重约束
use std::fmt::{Display, Debug};
// 使用 +
fn print_info(item: &(impl Summary + Display)) {
println!("{}", item.summarize());
println!("{}", item);
}
// Trait Bound 形式
fn print_info<T: Summary + Display>(item: &T) { /*...*/ }
// where 子句形式
fn complex<T, U>(t: &T, u: &U) -> String
where
T: Summary + Clone,
U: Display + Debug,
{
format!("{} - {:?}", t.summarize(), u)
}
Trait 作为返回值
// 返回实现了 Summary 的某个类型
fn create_summary() -> impl Summary {
Tweet {
username: String::from("test"),
content: String::from("hello"),
}
}
// ⚠️ 限制:只能返回单一类型
fn create_summary(switch: bool) -> impl Summary {
if switch {
Article { /* ... */ } // ❌ 错误!
} else {
Tweet { /* ... */ } // 不能返回不同类型
}
}
常用标准库 Trait
┌─────────────────┬────────────────────────────────────────────┐
│ Trait │ 用途 │
├─────────────────┼────────────────────────────────────────────┤
│ Clone │ 显式深拷贝 (.clone()) │
│ Copy │ 隐式按位复制(栈上简单类型) │
│ Debug │ 调试格式化 ({:?}) │
│ Display │ 用户友好格式化 ({}) │
│ Default │ 默认值 │
│ PartialEq / Eq │ 相等比较 (== !=) │
│ PartialOrd / Ord│ 排序比较 (< > <= >=) │
│ Hash │ 哈希计算 │
│ Drop │ 析构函数 │
│ From / Into │ 类型转换 │
│ AsRef / AsMut │ 引用转换 │
│ Deref / DerefMut│ 解引用运算符重载 │
│ Iterator │ 迭代器 │
│ Fn/FnMut/FnOnce │ 闭包 │
│ Send / Sync │ 线程安全标记 │
└─────────────────┴────────────────────────────────────────────┘
派生宏 (derive)
// 自动实现常见 trait
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone();
println!("{:?}", p1); // Debug
println!("{}", p1 == p2); // PartialEq → true
let p3 = Point::default(); // Default → Point { x: 0, y: 0 }
}
可派生的 Trait
#[derive(
Debug, // 调试输出
Clone, // 克隆
Copy, // 复制(需要 Clone)
PartialEq, // 部分相等
Eq, // 完全相等(需要 PartialEq)
PartialOrd, // 部分排序
Ord, // 完全排序(需要 PartialOrd + Eq)
Hash, // 哈希(需要 Eq)
Default, // 默认值
)]
struct Data { /* ... */ }
关联类型
// 使用关联类型(每个实现只有一种类型)
trait Iterator {
type Item; // 关联类型
fn next(&mut self) -> Option<Self::Item>;
}
struct Counter {
count: u32,
}
impl Iterator for Counter {
type Item = u32; // 指定具体类型
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count <= 5 {
Some(self.count)
} else {
None
}
}
}
关联类型 vs 泛型参数
// 关联类型:一个类型只能实现一次
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
// 泛型参数:一个类型可以多次实现
trait From<T> {
fn from(value: T) -> Self;
}
// String 可以实现多个 From
impl From<&str> for String { /*...*/ }
impl From<char> for String { /*...*/ }
impl From<Vec<u8>> for String { /*...*/ }
孤儿规则 (Orphan Rule)
// ✅ 可以:为自己的类型实现外部 trait
impl Display for MyStruct { /*...*/ }
// ✅ 可以:为外部类型实现自己的 trait
impl MyTrait for Vec<i32> { /*...*/ }
// ❌ 不行:为外部类型实现外部 trait
impl Display for Vec<i32> { /*...*/ } // 编译错误!
// 绕过方式:Newtype 模式
struct Wrapper(Vec<String>);
impl Display for Wrapper {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
Supertraits(特征继承)
use std::fmt::Display;
// OutlinePrint 要求类型必须也实现 Display
trait OutlinePrint: Display {
fn outline_print(&self) {
let output = self.to_string(); // 可以使用 Display 的方法
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("* {} *", output);
println!("{}", "*".repeat(len + 4));
}
}
// 实现时必须同时满足 Display
#[derive(Debug)]
struct Point { x: i32, y: i32 }
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
impl OutlinePrint for Point {} // 现在可以实现
完全限定语法
完全限定语法用于明确指定调用哪个类型或 trait 的方法/函数,解决名称冲突问题。
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) { println!("飞机起飞"); }
}
impl Wizard for Human {
fn fly(&self) { println!("魔法飞行"); }
}
impl Human {
fn fly(&self) { println!("挥动双臂"); }
}
fn main() {
let person = Human;
person.fly(); // 调用 Human::fly → "挥动双臂"
Pilot::fly(&person); // 调用 Pilot::fly → "飞机起飞"
Wizard::fly(&person); // 调用 Wizard::fly → "魔法飞行"
// 完全限定语法(用于关联函数)
// <Type as Trait>::function(args)
<Human as Pilot>::fly(&person);
}
关联常量
trait Greet {
const GREETING: &'static str = "Hello"; // 可有默认值
fn greet(&self) {
println!("{}", Self::GREETING);
}
}
struct Japanese;
impl Greet for Japanese {
const GREETING: &'static str = "こんにちは";
}
条件实现 (Blanket Implementation)
use std::fmt::Display;
// 为所有实现了 Display 的类型自动实现 ToString
impl<T: Display> ToString for T {
fn to_string(&self) -> String {
format!("{}", self)
}
}
// 条件方法实现
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Pair { x, y }
}
}
// 仅当 T 实现了 Display + PartialOrd 时才有 cmp_display
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("最大: {}", self.x);
} else {
println!("最大: {}", self.y);
}
}
}
集合类型
┌─────────────────┬──────────────┬───────────┬─────────────────────────┐
│ 类型 │ 底层结构 │ 有序 │ 主要用途 │
├─────────────────┼──────────────┼───────────┼─────────────────────────┤
│ Vec<T> │ 动态数组 │ 插入序 │ 通用序列 │
│ VecDeque<T> │ 环形缓冲 │ 插入序 │ 双端队列 │
│ LinkedList<T> │ 双向链表 │ 插入序 │ 频繁分割合并 │
├─────────────────┼──────────────┼───────────┼─────────────────────────┤
│ HashMap<K,V> │ 哈希表 │ ✗ │ 快速键值查找 │
│ BTreeMap<K,V> │ B-Tree │ 键有序 │ 有序映射/范围查询 │
├─────────────────┼──────────────┼───────────┼─────────────────────────┤
│ HashSet<T> │ 哈希表 │ ✗ │ 快速去重/集合运算 │
│ BTreeSet<T> │ B-Tree │ 有序 │ 有序集合/范围查询 │
├─────────────────┼──────────────┼───────────┼─────────────────────────┤
│ BinaryHeap<T> │ 二叉堆 │ 堆序 │ 优先队列/Top K │
└─────────────────┴──────────────┴───────────┴─────────────────────────┘
序列
Vec\<T>
动态数组,连续内存存储,最常用的集合类型。
fn main() {
// 创建
let mut v1: Vec<i32> = Vec::new();
let mut v2 = vec![1, 2, 3]; // 宏创建
let v3 = vec![0; 5]; // [0, 0, 0, 0, 0]
let v4: Vec<i32> = Vec::with_capacity(10); // 预分配容量
// 增删
v2.push(4); // 尾部添加
v2.pop(); // 尾部弹出 → Some(4)
v2.insert(0, 0); // 指定位置插入
v2.remove(0); // 指定位置删除
// 访问
let third = &v2[2]; // 索引(越界 panic)
let third = v2.get(2); // 返回 Option(安全)
// 遍历
for item in &v2 { /* 只读 */ }
for item in &mut v2 { *item += 1; } // 可修改
}
| 操作 | 时间复杂度 |
|---|---|
| push / pop | O(1) 均摊 |
| insert / remove | O(n) |
| 索引访问 | O(1) |
VecDeque\<T>
双端队列,基于环形缓冲区,两端操作高效。
use std::collections::VecDeque;
// 创建
let mut deque: VecDeque<i32> = VecDeque::new();
let mut deque = VecDeque::from([1, 2, 3]);
// 双端操作
deque.push_front(0); // 头部添加
deque.push_back(4); // 尾部添加
deque.pop_front(); // 头部弹出 → Some(0)
deque.pop_back(); // 尾部弹出 → Some(4)
// 访问
let first = deque.front(); // 查看头部
let last = deque.back(); // 查看尾部
let item = deque.get(1); // 索引访问
| 操作 | 时间复杂度 |
|---|---|
| push_front / push_back | O(1) 均摊 |
| pop_front / pop_back | O(1) |
| 索引访问 | O(1) |
适用场景:队列、滑动窗口、需要两端频繁操作
LinkedList\<T>
双向链表,非连续存储,任意位置插入删除高效。
use std::collections::LinkedList;
// 创建
let mut list: LinkedList<i32> = LinkedList::new();
let mut list = LinkedList::from([1, 2, 3]);
// 双端操作
list.push_front(0);
list.push_back(4);
list.pop_front();
list.pop_back();
// 链表特有操作
let mut other = LinkedList::from([5, 6]);
list.append(&mut other); // 合并链表(O(1))
// 游标操作(nightly)
// let mut cursor = list.cursor_front_mut();
| 操作 | 时间复杂度 |
|---|---|
| push / pop(两端) | O(1) |
| append(合并) | O(1) |
| 索引访问 | O(n) ⚠️ |
对比总结
┌──────────────┬────────────┬────────────┬──────────────┐
│ 操作 │ Vec<T> │ VecDeque<T>│ LinkedList<T>│
├──────────────┼────────────┼────────────┼──────────────┤
│ 头部插入/删除│ O(n) │ O(1) │ O(1) │
│ 尾部插入/删除│ O(1) │ O(1) │ O(1) │
│ 中间插入/删除│ O(n) │ O(n) │ O(1)* │
│ 随机访问 │ O(1) │ O(1) │ O(n) │
│ 内存布局 │ 连续 │ 连续 │ 分散 │
│ 缓存友好 │ ✓✓ │ ✓ │ ✗ │
└──────────────┴────────────┴────────────┴──────────────┘
映射
HashMap\<K, V>
哈希映射,基于哈希表实现,无序存储,查找极快。
use std::collections::HashMap;
// 创建
let mut map: HashMap<String, i32> = HashMap::new();
let mut map = HashMap::from([("a", 1), ("b", 2)]);
let map: HashMap<_, _> = vec![("a", 1), ("b", 2)].into_iter().collect();
// 插入与更新
map.insert("c", 3); // 插入/覆盖
map.entry("d").or_insert(4); // 不存在才插入
map.entry("c").or_insert_with(|| expensive_calc()); // 惰性计算
map.entry("c").and_modify(|v| *v += 10); // 存在则修改
// 访问
let val = map.get("a"); // Option<&V>
let val = map.get_mut("a"); // Option<&mut V>
let val = map["a"]; // 直接取值
let has = map.contains_key("a"); // 判断存在
// 删除
map.remove("a"); // 删除键
map.remove_entry("b"); // 删除并返回键值对
// 遍历(顺序不确定)
for (key, val) in &map { /* ... */ }
for key in map.keys() { /* ... */ }
for val in map.values_mut() { *val += 1; }
| 操作 | 平均复杂度 | 最差复杂度 |
|---|---|---|
| 插入 / 删除 | O(1) | O(n) |
| 查找 | O(1) | O(n) |
| 遍历 | O(n) | O(n) |
键的要求:必须实现 Eq + Hash
惰性计算
or_insert(value)
────────────────────────────────────────
- 先计算 value ← 总是执行!
- 检查 key 是否存在
- 不存在 → 插入 value
存在 → 丢弃 value(浪费了计算)
or_insert_with(|| closure)
────────────────────────────────────────
- 检查 key 是否存在
- 不存在 → 执行闭包 → 插入结果
存在 → 什么都不做 ← 节省计算!
BTreeMap\<K, V>
B树映射,基于 B-Tree 实现,有序存储,范围查询高效。
use std::collections::BTreeMap;
// 创建
let mut map: BTreeMap<String, i32> = BTreeMap::new();
let mut map = BTreeMap::from([("a", 1), ("c", 3), ("b", 2)]);
// 基本操作(与 HashMap 相同)
map.insert("d", 4);
map.get("a");
map.remove("a");
map.entry("e").or_insert(5);
// 有序特性
let first = map.first_key_value(); // 最小键值对
let last = map.last_key_value(); // 最大键值对
map.pop_first(); // 弹出最小
map.pop_last(); // 弹出最大
// 范围查询(独有优势)
for (k, v) in map.range("b".."d") { // 左闭右开
println!("{}: {}", k, v);
}
for (k, v) in map.range("b"..="d") { // 左闭右闭
println!("{}: {}", k, v);
}
// 遍历(按键升序)
for (key, val) in &map { /* 有序遍历 */ }
| 操作 | 时间复杂度 |
|---|---|
| 插入 / 删除 | O(log n) |
| 查找 | O(log n) |
| 范围查询 | O(log n + k)* |
| 最小/最大 | O(log n) |
*k 为范围内元素数量
键的要求:必须实现 Ord
对比总结
┌──────────────────┬─────────────────┬─────────────────┐
│ 特性 │ HashMap<K,V> │ BTreeMap<K,V> │
├──────────────────┼─────────────────┼─────────────────┤
│ 底层结构 │ 哈希表 │ B-Tree │
│ 元素顺序 │ 无序 │ 按键有序 │
│ 查找复杂度 │ O(1) │ O(log n) │
│ 插入复杂度 │ O(1) │ O(log n) │
│ 范围查询 │ ✗ │ ✓ │
│ 最小/最大键 │ O(n) │ O(log n) │
│ 键的约束 │ Eq + Hash │ Ord │
│ 内存占用 │ 较大 │ 较小 │
│ 缓存友好 │ ✓ │ ✓ │
└──────────────────┴─────────────────┴─────────────────┘
选择建议:
| 场景 | 推荐 |
|---|---|
| 快速查找,顺序无关 | HashMap |
| 需要有序遍历 | BTreeMap |
| 范围查询(如区间统计) | BTreeMap |
| 频繁取最大/最小 | BTreeMap |
| 键不支持 Hash | BTreeMap |
| 默认选择 | HashMap |
集合
HashSet\<T>
哈希集合,基于 HashMap 实现,无序存储,元素唯一,查找极快。
use std::collections::HashSet;
// 创建
let mut set: HashSet<i32> = HashSet::new();
let mut set = HashSet::from([1, 2, 3]);
let set: HashSet<_> = vec![1, 2, 2, 3].into_iter().collect(); // 自动去重
// 增删
set.insert(4); // 插入,返回 bool(是否新增)
set.remove(&2); // 删除,返回 bool
set.take(&3); // 删除并返回 Option<T>
// 查询
set.contains(&1); // 是否存在
set.get(&1); // 获取引用 Option<&T>
set.len(); // 元素数量
set.is_empty(); // 是否为空
// 遍历(顺序不确定)
for item in &set { /* ... */ }
集合运算(HashSet 独有优势):
let a: HashSet<_> = [1, 2, 3, 4].into();
let b: HashSet<_> = [3, 4, 5, 6].into();
// 交集
let intersection: HashSet<_> = a.intersection(&b).collect(); // {3, 4}
let intersection: HashSet<_> = &a & &b; // 运算符形式
// 并集
let union: HashSet<_> = a.union(&b).collect(); // {1,2,3,4,5,6}
let union: HashSet<_> = &a | &b;
// 差集
let difference: HashSet<_> = a.difference(&b).collect(); // {1, 2}
let difference: HashSet<_> = &a - &b;
// 对称差集(并集 - 交集)
let sym_diff: HashSet<_> = a.symmetric_difference(&b).collect(); // {1,2,5,6}
let sym_diff: HashSet<_> = &a ^ &b;
// 子集/超集判断
a.is_subset(&b); // a ⊆ b ?
a.is_superset(&b); // a ⊇ b ?
a.is_disjoint(&b); // 是否无交集
| 操作 | 平均复杂度 |
|---|---|
| 插入 / 删除 | O(1) |
| 查找 | O(1) |
| 集合运算 | O(n) |
元素要求:必须实现 Eq + Hash
BTreeSet\<T>
B树集合,基于 BTreeMap 实现,有序存储,元素唯一。
use std::collections::BTreeSet;
fn main() {
// 创建
let mut set = BTreeSet::from([3, 1, 4, 1, 5]); // 自动去重排序
println!("set: {:?}", set);
// 基本操作(与 HashSet 相同)
set.insert(2);
println!("set: {:?}", set);
set.remove(&3);
println!("set: {:?}", set);
if set.contains(&1) {
println!("set contains 1");
} else {
println!("set does not contain 1");
}
// 有序特性
let first = set.first(); // 最小元素
let last = set.last();
println!("first: {:?}, last: {:?}", first, last);
set.pop_first(); // 弹出最小
set.pop_last(); // 弹出最大
println!("set: {:?}", set);
// 范围查询(独有优势)
for x in set.range(2..5) { // 范围遍历
println!("{}", x);
}
for num in 0..=5 {
set.insert(num);
}
// 分割
println!("set: {:?}", set);
let high = set.split_off(&3); // 分割成 [..3) 和 [3..]
println!("high: {:?}", high);
let low = set;
println!("low: {:?}", low);
// 集合运算(同样支持)
let a = BTreeSet::from([1, 2, 3]);
let b = BTreeSet::from([2, 3, 4]);
let union: BTreeSet<_> = a.union(&b).cloned().collect();
println!("union: {:?}", union);
}
| 操作 | 时间复杂度 |
|---|---|
| 插入 / 删除 | O(log n) |
| 查找 | O(log n) |
| 最小 / 最大 | O(log n) |
| 范围查询 | O(log n + k) |
元素要求:必须实现 Ord
集合对比
┌──────────────────┬─────────────────┬─────────────────┐
│ 特性 │ HashSet<T> │ BTreeSet<T> │
├──────────────────┼─────────────────┼─────────────────┤
│ 底层结构 │ 哈希表 │ B-Tree │
│ 元素顺序 │ 无序 │ 有序 │
│ 查找复杂度 │ O(1) │ O(log n) │
│ 插入复杂度 │ O(1) │ O(log n) │
│ 范围查询 │ ✗ │ ✓ │
│ 最小/最大 │ O(n) │ O(log n) │
│ 元素约束 │ Eq + Hash │ Ord │
└──────────────────┴─────────────────┴─────────────────┘
其他
BinaryHeap\<T>
二叉堆,基于数组的完全二叉树,实现优先队列,默认最大堆。
use std::collections::BinaryHeap;
// 创建
let mut heap: BinaryHeap<i32> = BinaryHeap::new();
let mut heap = BinaryHeap::from([3, 1, 4, 1, 5]);
// 入堆出堆
heap.push(9); // 添加元素
heap.pop(); // 弹出最大值 → Some(9)
heap.peek(); // 查看最大值(不弹出)
heap.peek_mut(); // 可修改的最大值引用
// 容量操作
heap.len();
heap.is_empty();
heap.capacity();
heap.reserve(10);
// 转换
let vec = heap.into_vec(); // 转为无序 Vec
let sorted = heap.into_sorted_vec(); // 转为升序 Vec
// 遍历(⚠️ 顺序不保证完全有序)
for item in &heap { /* 不保证顺序 */ }
// 有序消费
while let Some(max) = heap.pop() {
println!("{}", max); // 从大到小
}
最小堆实现:
use std::collections::BinaryHeap;
use std::cmp::Reverse;
// 方法1:使用 Reverse 包装
let mut min_heap = BinaryHeap::new();
min_heap.push(Reverse(3));
min_heap.push(Reverse(1));
min_heap.push(Reverse(4));
if let Some(Reverse(min)) = min_heap.pop() {
println!("最小值: {}", min); // 1
}
// 方法2:自定义类型实现反向 Ord
#[derive(Eq, PartialEq)]
struct MinInt(i32);
impl Ord for MinInt {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.0.cmp(&self.0) // 反向比较
}
}
impl PartialOrd for MinInt {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
经典应用:
// Top K 问题:找最大的 K 个元素
fn top_k(nums: Vec<i32>, k: usize) -> Vec<i32> {
let mut heap = BinaryHeap::from(nums);
(0..k).filter_map(|_| heap.pop()).collect()
}
// 合并 K 个有序数组
fn merge_k_sorted(arrays: Vec<Vec<i32>>) -> Vec<i32> {
let mut heap = BinaryHeap::new();
let mut result = Vec::new();
// (Reverse(值), 数组索引, 元素索引)
for (i, arr) in arrays.iter().enumerate() {
if !arr.is_empty() {
heap.push(Reverse((arr[0], i, 0)));
}
}
while let Some(Reverse((val, arr_idx, elem_idx))) = heap.pop() {
result.push(val);
if elem_idx + 1 < arrays[arr_idx].len() {
heap.push(Reverse((arrays[arr_idx][elem_idx + 1], arr_idx, elem_idx + 1)));
}
}
result
}
// 任务调度(按优先级)
#[derive(Eq, PartialEq)]
struct Task {
priority: u32,
name: String,
}
impl Ord for Task {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.priority.cmp(&other.priority)
}
}
impl PartialOrd for Task {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
let mut tasks = BinaryHeap::new();
tasks.push(Task { priority: 1, name: "低优先级".into() });
tasks.push(Task { priority: 10, name: "高优先级".into() });
// pop 时先取出高优先级任务
| 操作 | 时间复杂度 |
|---|---|
| push | O(log n) |
| pop | O(log n) |
| peek | O(1) |
| heapify (from) | O(n) |
元素要求:必须实现 Ord
生命周期
一句话定义
生命周期 = 引用保持有效的作用域范围
编译器用它确保:引用永远不会比它指向的数据活得更久
为什么需要生命周期?
fn main() {
let r; // ---------+-- 'a
{ // |
let x = 5; // -+-- 'b |
r = &x; // | | ❌ x 的生命周期 'b 比 r 的 'a 短
} // -+ |
println!("{}", r); // 悬垂引用!|
} // ---------+
编译器通过生命周期分析,在编译期阻止悬垂引用。
生命周期注解语法
&i32 // 普通引用
&'a i32 // 带生命周期 'a 的引用
&'a mut i32 // 带生命周期 'a 的可变引用
注解不改变生命周期长短,只是告诉编译器多个引用之间的关系
函数中的生命周期
问题场景
// ❌ 编译失败:返回值的生命周期是什么?
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
解决方案
// ✅ 告诉编译器:返回值的生命周期与输入相同
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
理解 'a 的含义
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
// ↑ ↑ ↑ ↑
// 声明 x 至少活 'a y 至少活 'a 返回值在 'a 内有效
// 实际调用时,'a = x 和 y 生命周期的交集(较短者)
x: |=================|
y: |==========|
'a:|==========| ← 取交集
结构体中的生命周期
// 结构体持有引用时必须标注生命周期
struct Excerpt<'a> {
part: &'a str, // part 引用的数据必须比 Excerpt 实例活得久
}
fn main() {
let novel = String::from("Call.me.Ishmael");
// ↑ novel 所有权开始
let first = novel.split('.').next().unwrap();
// ↑ first: &str,借用 novel(不可变借用开始)
println!("{:?}", first);
let excerpt = Excerpt { part: first };
// ↑ excerpt.part 继承了对 novel 的借用
println!("{:?}", excerpt.part);
// ↑ 最后一次使用借用
// ↓ NLL: 借用在这里结束
// 此时可以这样做(如果 novel 是 mut):
// novel.push_str("..."); ✅ 借用已结束
} // ← novel 被 drop,所有权结束
生命周期省略规则
编译器自动推断的三条规则:
// 规则1:每个引用参数获得独立的生命周期
fn foo(x: &str, y: &str)
// 推断为
fn foo<'a, 'b>(x: &'a str, y: &'b str)
// 规则2:只有一个输入生命周期,它被赋给所有输出
fn foo(x: &str) -> &str
// 推断为
fn foo<'a>(x: &'a str) -> &'a str
// 规则3:方法中 &self 的生命周期赋给所有输出
impl Foo {
fn bar(&self, x: &str) -> &str
// 推断为
fn bar<'a, 'b>(&'a self, x: &'b str) -> &'a str
}
何时必须手动标注?
// ✅ 可省略:只有一个输入引用
fn first_word(s: &str) -> &str { ... }
// ❌ 必须标注:多个输入,编译器不知道输出跟谁关联
fn longest(x: &str, y: &str) -> &str { ... } // 报错
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ... } // ✅
静态生命周期 'static
// 'static = 整个程序运行期间都有效
let s: &'static str = "hello"; // 字符串字面量
// 常见场景
const NAME: &'static str = "Rust";
static VERSION: &'static str = "1.0";
// ⚠️ 不要滥用 'static 来"解决"生命周期问题
常见模式速查
// 模式1:返回输入之一
fn pick<'a>(x: &'a str, y: &'a str, flag: bool) -> &'a str {
if flag { x } else { y }
}
// 模式2:只关联部分输入
fn first<'a>(x: &'a str, _y: &str) -> &'a str {
x // 返回值只与 x 关联
}
// 模式3:返回新创建的数据(无需生命周期)
fn create(x: &str) -> String {
x.to_uppercase() // 返回 owned 数据,不是引用
}
// 模式4:结构体 + impl
struct Parser<'a> {
input: &'a str,
}
impl<'a> Parser<'a> {
fn new(input: &'a str) -> Self {
Parser { input }
}
fn parse(&self) -> &'a str {
&self.input[0..5]
}
}
生命周期子类型
// 'a: 'b 表示 'a 至少和 'b 一样长
fn foo<'a, 'b>(x: &'a str, y: &'b str) -> &'b str
where
'a: 'b, // 'a 不短于 'b
{
if x.len() > 0 { y } else { y }
}
常见错误与修复
// ❌ 错误:返回局部变量的引用
fn bad() -> &str {
let s = String::from("hello");
&s // s 在函数结束时被释放
}
// ✅ 修复:返回 owned 数据
fn good() -> String {
String::from("hello")
}
// ❌ 错误:结构体比数据活得久
fn bad_struct() -> Excerpt {
let s = String::from("hello");
Excerpt { part: &s } // s 即将被释放
}
// ✅ 修复:确保数据在外部
fn good_struct(s: &str) -> Excerpt {
Excerpt { part: s }
}
心智模型
┌─────────────────────────────────────────────┐
│ 生命周期 = 编译器的"借用有效期检查器" │
├─────────────────────────────────────────────┤
│ 1. 所有引用都有生命周期(大多自动推断) │
│ 2. 注解只是描述关系,不改变实际生命周期 │
│ 3. 目的:编译期消除悬垂引用 │
│ 4. 遇到报错 → 思考"谁应该活得更久?" │
└─────────────────────────────────────────────┘
| 场景 | 是否需要标注 |
|---|---|
| 函数单个引用输入输出 | ❌ 自动推断 |
| 函数多个引用输入 + 引用输出 | ✅ 需要 |
| 结构体持有引用 | ✅ 需要 |
| 返回 owned 数据 | ❌ 无需 |
&'static |
全局/字面量 |
包和模块
层级关系
Package(包)
├── Cargo.toml(包的配置)
└── Crate(编译单元)
├── src/main.rs(二进制 crate 根)
├── src/lib.rs(库 crate 根)
└── Module(模块,代码组织单元)
└── 函数、结构体、枚举...
Package(包)
cargo new my_project # 创建包
my_project/
├── Cargo.toml # 包配置(名称、依赖等)
├── src/
│ ├── main.rs # 二进制 crate(可选)
│ └── lib.rs # 库 crate(可选)
└── src/bin/ # 多个二进制 crate(可选)
├── tool1.rs
└── tool2.rs
规则:一个包最多 1 个库 crate,可以有多个二进制 crate
Module(模块)
定义方式
// 方式1:内联定义
mod math {
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub mod advanced { // 嵌套模块
pub fn pow(base: i32, exp: u32) -> i32 { base.pow(exp) }
}
}
// 方式2:从文件加载(见后文)
mod utils; // 加载 utils.rs 或 utils/mod.rs
模块树
// src/lib.rs
mod front {
pub mod hosting {
pub fn seat() {}
}
mod serving { // 私有模块
fn serve() {}
}
}
// 生成的模块树:
// crate (根)
// └── front
// ├── hosting
// │ └── seat
// └── serving (私有)
// └── serve (私有)
路径与可见性
路径类型
// 绝对路径:从 crate 根开始
crate::front::hosting::seat();
// 相对路径:从当前模块开始
front::hosting::seat();
// self:当前模块
self::some_function();
// super:父模块
super::parent_function();
可见性规则
mod outer {
pub mod inner {
pub fn public_fn() {} // ✅ 外部可访问
fn private_fn() {} // ❌ 仅 inner 内可访问
pub struct Point {
pub x: i32, // ✅ 公开字段
y: i32, // ❌ 私有字段
}
pub enum Status { // 枚举变体自动公开
Active, // ✅
Inactive, // ✅
}
}
fn outer_fn() {
inner::public_fn(); // ✅
inner::private_fn(); // ✅ 父模块可访问子模块私有项?
// ❌ 错!私有就是私有
}
}
高级可见性
pub(crate) fn crate_only() {} // 仅当前 crate 内可见
pub(super) fn parent_only() {} // 仅父模块可见
pub(in crate::path) fn limited() {} // 指定路径内可见
use 关键字
// 基本引入
use std::collections::HashMap;
// 嵌套引入
use std::collections::{HashMap, HashSet};
use std::io::{self, Read, Write}; // self = std::io
// 全部引入(谨慎使用)
use std::collections::*;
// 重命名
use std::io::Result as IoResult;
// 重导出(pub use)
pub use crate::utils::helper; // 外部可通过此路径访问
惯用法
// 函数:引入父模块
use std::io;
io::stdin(); // 清晰表明来源
// 类型:直接引入
use std::collections::HashMap;
let map = HashMap::new();
常见模式
预导入模式
// src/lib.rs
pub mod prelude {
pub use crate::types::{Error, Result};
pub use crate::traits::{Read, Write};
}
// 使用者
use my_crate::prelude::*;
重导出简化路径
// src/lib.rs(内部结构复杂)
mod deep {
pub mod nested {
pub struct Important;
}
}
// 在顶层重导出
pub use deep::nested::Important;
// 外部直接使用
use my_crate::Important; // 而非 my_crate::deep::nested::Important
测试模块
pub fn add(a: i32, b: i32) -> i32 { a + b }
#[cfg(test)]
mod tests {
use super::*; // 引入父模块所有项
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
}
速查表
| 关键字 | 作用 |
|---|---|
mod x |
定义/加载模块 x |
pub |
公开可见 |
use |
引入路径 |
pub use |
重导出 |
crate:: |
绝对路径 |
self:: |
当前模块 |
super:: |
父模块 |
心智模型
┌──────────────────────────────────────────────────┐
│ Package │
│ ┌─────────────────────────────────────────────┐ │
│ │ Crate (lib.rs / main.rs) │ │
│ │ ┌─────────────────────────────────────────┐│ │
│ │ │ crate (根模块) ││ │
│ │ │ ├── mod A (pub) ││ │
│ │ │ │ ├── fn x (pub) ← crate::A::x ││ │
│ │ │ │ └── fn y ← 私有 ││ │
│ │ │ └── mod B ││ │
│ │ │ └── mod C (pub) ││ │
│ │ │ └── fn z ← crate::B::C::z ││ │
│ │ └─────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
访问规则:
1. 默认私有,需要 pub 公开
2. 父可访问子的公开项
3. 子可访问父(及祖先)的所有项
格式化输出
核心宏家族
| 宏 | 用途 | 输出位置 |
|---|---|---|
print! |
输出不换行 | stdout |
println! |
输出并换行 | stdout |
eprint! |
错误输出不换行 | stderr |
eprintln! |
错误输出并换行 | stderr |
format! |
返回 String |
不输出 |
write! |
写入 buffer | 指定位置 |
基础占位符
let name = "Rust";
let version = 1.75;
// {} - Display 格式(用户友好)
println!("Hello, {}!", name); // Hello, Rust!
// {:?} - Debug 格式(调试用)
println!("{:?}", vec![1, 2, 3]); // [1, 2, 3]
// {:#?} - Debug 美化格式
println!("{:#?}", vec![1, 2, 3]);
// [
// 1,
// 2,
// 3,
// ]
参数引用方式
// 1. 顺序引用
println!("{} + {} = {}", 1, 2, 3); // 1 + 2 = 3
// 2. 位置索引
println!("{0} vs {0}, {1} wins", "A", "B"); // A vs A, B wins
// 3. 命名参数
println!("{name} is {age} years old",
name = "Alice",
age = 30); // Alice is 30 years old
// 4. 变量捕获(Rust 1.58+)
let name = "Bob";
let age = 25;
println!("{name} is {age} years old"); // Bob is 25 years old
数值格式
let num = 255;
// 进制转换
println!("十进制: {}", num); // 255
println!("二进制: {:b}", num); // 11111111
println!("八进制: {:o}", num); // 377
println!("十六进制: {:x}", num); // ff
println!("十六进制: {:X}", num); // FF
// 带前缀
println!("{:#b}", num); // 0b11111111
println!("{:#o}", num); // 0o377
println!("{:#x}", num); // 0xff
// 指针地址
let x = 42;
println!("{:p}", &x); // 0x7ffd5e8e1234
// 科学计数法
let f = 1234.567;
println!("{:e}", f); // 1.234567e3
println!("{:E}", f); // 1.234567E3
宽度与对齐
let s = "Hi";
let n = 42;
// 宽度指定
println!("[{:10}]", s); // [Hi ] 默认左对齐(字符串)
println!("[{:10}]", n); // [ 42] 默认右对齐(数字)
// 对齐方式
println!("[{:<10}]", s); // [Hi ] 左对齐
println!("[{:>10}]", s); // [ Hi] 右对齐
println!("[{:^10}]", s); // [ Hi ] 居中
// 自定义填充字符
println!("[{:*<10}]", s); // [Hi********]
println!("[{:0>10}]", n); // [0000000042]
println!("[{:-^10}]", s); // [----Hi----]
// 动态宽度
let width = 8;
println!("[{:>width$}]", s); // [ Hi]
println!("[{:>1$}]", s, width); // [ Hi]
精度控制
// 浮点数精度
let pi = 3.14159265358979;
println!("{:.2}", pi); // 3.14
println!("{:.0}", pi); // 3
println!("{:.6}", pi); // 3.141593
// 宽度 + 精度
println!("{:10.2}", pi); // " 3.14"
println!("{:010.2}", pi); // "0000003.14"
// 字符串截断
let s = "Hello, World!";
println!("{:.5}", s); // Hello
// 动态精度
let precision = 3;
println!("{:.prec$}", pi, prec = precision); // 3.142
println!("{:.1$}", pi, precision); // 3.142
println!("{pi:.precision$}"); // 3.142
符号与特殊格式
let pos = 42;
let neg = -42;
// 正数显示符号
println!("{:+}", pos); // +42
println!("{:+}", neg); // -42
// 数字分隔(需 nightly 或手动)
// println!("{:_}", 1000000);
// 组合使用
println!("{:+010.2}", 3.14); // +000003.14
// │││││└── 精度 2 位小数
// ││││└─── 总宽度 10
// │││└──── 用 0 填充
// ││└───── 强制显示符号
// │└────── 对齐(默认右)
// └─────── 格式说明符开始
Debug 与 Display Trait
use std::fmt;
struct Point {
x: i32,
y: i32,
}
// 实现 Display(用户友好输出)
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
// 实现 Debug(调试输出)- 通常用 derive
#[derive(Debug)]
struct Point2 {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p); // (3, 4) - Display
// println!("{:?}", p); // 需要手动实现 Debug
let p2 = Point2 { x: 3, y: 4 };
println!("{:?}", p2); // Point2 { x: 3, y: 4 }
println!("{:#?}", p2); // 美化输出
}
自定义格式化细节
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 可以访问格式化选项
if f.alternate() { // {:#} 被使用时
write!(f, "Point[x={}, y={}]", self.x, self.y)
} else {
write!(f, "({}, {})", self.x, self.y)
}
}
}
let p = Point { x: 1, y: 2 };
println!("{}", p); // (1, 2)
println!("{:#}", p); // Point[x=1, y=2]
转义与原始输出
// 转义大括号
println!("{{}}"); // {}
println!("{{{}}}", 42); // {42}
// 原始字符串
println!(r"C:\Users\name"); // C:\Users\name
println!(r#"She said "Hi""#); // She said "Hi"
注释与文档
注释类型总览
| 类型 | 语法 | 用途 |
|---|---|---|
| 行注释 | // |
代码说明 |
| 块注释 | /* */ |
多行代码说明 |
| 文档注释(外部) | /// |
为下方项生成文档 |
| 文档注释(内部) | //! |
为所在项生成文档 |
普通注释
// 这是行注释,延伸到行尾
/* 这是块注释
可以跨越多行 */
let x = 1 /* 块注释可以内联 */ + 2;
/* 块注释
/* 可以嵌套 */
这在临时注释代码块时很有用
*/
文档注释
/// 外部文档(为下方项生成文档)
/// 计算两个数的和
///
/// # Examples
///
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
/// 表示二维平面上的点
///
/// # Fields
///
/// * `x` - X 坐标
/// * `y` - Y 坐标
pub struct Point {
pub x: f64,
pub y: f64,
}
//! 内部文档(为所在项生成文档)
// src/lib.rs 顶部
//! # My Awesome Crate
//!
//! `my_crate` 提供了一系列实用工具函数。
//!
//! ## 快速开始
//!
//! ```rust
//! use my_crate::add;
//! let sum = add(1, 2);
//! ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// 模块内部文档
pub mod utils {
//! 工具函数模块
//!
//! 提供字符串处理、数值计算等实用功能。
/// 将字符串转为大写
pub fn to_upper(s: &str) -> String {
s.to_uppercase()
}
}
Markdown 支持
/// # 一级标题
/// ## 二级标题
///
/// 普通段落,支持 **粗体**、*斜体*、`行内代码`。
///
/// ## 列表
///
/// - 无序列表项 1
/// - 无序列表项 2
/// - 嵌套项
///
/// 1. 有序列表项 1
/// 2. 有序列表项 2
///
/// ## 代码块
///
/// ```rust
/// let x = 42;
/// println!("{}", x);
/// ```
///
/// ## 链接
///
/// [Rust 官网](https://www.rust-lang.org)
///
/// ## 文档内链接
///
/// 参见 [`HashMap`](std::collections::HashMap)
/// 或者 [`crate::utils::helper`]
pub fn example() {}
常用文档节(Sections)
/// 执行网络请求
///
/// # Arguments
///
/// * `url` - 请求的目标 URL
/// * `timeout` - 超时时间(秒)
///
/// # Returns
///
/// 返回响应内容的字符串
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let response = my_crate::fetch("https://example.com", 30)?;
/// println!("{}", response);
/// # Ok(())
/// # }
/// ```
///
/// # Panics
///
/// 当 `timeout` 为 0 时会 panic
///
/// # Errors
///
/// - 网络不可达时返回 `NetworkError`
/// - URL 无效时返回 `InvalidUrlError`
///
/// # Safety
///
/// (用于 unsafe 函数)说明安全使用的前提条件
///
/// # See Also
///
/// - [`fetch_async`] - 异步版本
/// - [`post`] - POST 请求
pub fn fetch(url: &str, timeout: u32) -> Result<String, Error> {
// ...
}
标准节名称
| 节名 | 用途 |
|---|---|
# Examples |
使用示例(可被测试) |
# Panics |
何时会 panic |
# Errors |
返回的错误类型及条件 |
# Safety |
unsafe 代码的安全条件 |
# Arguments |
参数说明 |
# Returns |
返回值说明 |
# See Also |
相关项引用 |
代码块与文档测试
基本文档测试
/// 计算阶乘
///
/// ```
/// assert_eq!(my_crate::factorial(5), 120);
/// assert_eq!(my_crate::factorial(0), 1);
/// ```
pub fn factorial(n: u64) -> u64 {
(1..=n).product()
}
cargo test --doc # 运行文档测试
代码块标记
/// ```rust
/// // 明确指定语言(可省略,默认 Rust)
/// let x = 1;
/// ```
///
/// ```ignore
/// // 不编译、不运行(语法可能无效)
/// this is not valid rust!
/// ```
///
/// ```no_run
/// // 编译但不运行(如无限循环、需要网络等)
/// loop { /* 永远运行 */ }
/// ```
///
/// ```compile_fail
/// // 预期编译失败(演示错误)
/// let x: i32 = "string";
/// ```
///
/// ```should_panic
/// // 预期 panic
/// panic!("This should panic");
/// ```
///
/// ```text
/// 这是纯文本,不会被当作代码处理
/// 用于展示输出结果等
/// ```
pub fn example() {}
隐藏辅助代码
/// 读取配置文件
///
/// ```
/// # // 以 # 开头的行在文档中隐藏,但会被编译和测试
/// # use std::fs;
/// # fn main() -> Result<(), std::io::Error> {
/// let config = my_crate::read_config("app.toml")?;
/// println!("{:?}", config);
/// # Ok(())
/// # }
/// ```
pub fn read_config(path: &str) -> Result<Config, std::io::Error> {
// ...
}
渲染结果只显示:
let config = my_crate::read_config("app.toml")?;
println!("{:?}", config);
文档链接
/// 使用 [`Vec`] 存储数据
///
/// 也可以用 [`Option`] 和 [`Result`]
///
/// 自定义类型链接:[`MyStruct`]
///
/// 指定方法:[`Vec::push`]
///
/// 完整路径:[`std::collections::HashMap`]
///
/// 带显示文本:[点击这里](crate::module::function)
pub struct MyStruct;
/// 链接到其他项
///
/// - 关联函数:[`String::from`]
/// - 方法:[`String::len`]
/// - 模块:[`std::collections`]
/// - 宏:[`println!`]
/// - 外部链接:[Rust Book](https://doc.rust-lang.org/book/)
pub fn doc_links() {}
属性与配置
#[doc] 属性
// 等价于 /// 文档内容
#[doc = "这是文档注释"]
pub fn foo() {}
// 包含外部文件作为文档
#[doc = include_str!("../README.md")]
pub struct MyLib;
// 隐藏项(不在文档中显示)
#[doc(hidden)]
pub fn internal_use_only() {}
// 给文档添加别名(搜索时可用)
#[doc(alias = "create")]
#[doc(alias = "make")]
pub fn new() -> Self { /* ... */ }
Cargo.toml 文档配置
[package]
name = "my_crate"
documentation = "https://docs.rs/my_crate"
readme = "README.md"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
生成与查看文档
# 生成文档
cargo doc
# 生成并打开浏览器
cargo doc --open
# 包含私有项
cargo doc --document-private-items
# 包含依赖的文档
cargo doc --no-deps # 不包含依赖(默认包含)
# 运行文档测试
cargo test --doc
函数式编程
核心概念
函数式编程三要素
├── 闭包(Closure) → 匿名函数,可捕获环境
├── 迭代器(Iterator) → 惰性序列处理
└── 组合器(Combinator)→ 链式数据转换
闭包(Closure)
基本语法
// 完整形式
let add = |a: i32, b: i32| -> i32 { a + b };
// 简化形式(类型推断)
let add = |a, b| a + b;
// 无参数
let greet = || println!("Hello!");
// 多行
let complex = |x| {
let y = x * 2;
y + 1
};
println!("{}", add(2, 3)); // 5
greet(); // Hello!
捕获环境变量
Rust 编译器根据闭包内如何使用变量自动决定捕获方式,选择"刚好够用"的最小权限。
let x = 10;
let y = 20;
// 自动捕获外部变量
let sum = || x + y;
println!("{}", sum()); // 30
// 带参数 + 捕获
let add_x = |n| n + x;
println!("{}", add_x(5)); // 15
三种捕获方式
// 1. 不可变借用(Fn)
let s = String::from("hello");
let print = || println!("{}", s); // 借用 s
print();
print(); // ✅ 可多次调用
println!("{}", s); // ✅ s 仍可用
// 2. 可变借用(FnMut)
let mut count = 0;
let mut inc = || count += 1; // 可变借用 count
inc();
inc();
println!("{}", count); // 2
// 3. 获取所有权(FnOnce)
let s = String::from("hello");
let consume = || drop(s); // 消耗 s
consume();
// consume(); // ❌ 不能再次调用
// println!("{}", s); // ❌ s 已被移动
Trait 对应关系
┌─────────────────────────────────────────────────────┐
│ 捕获方式 Trait 可调用次数 签名 │
├─────────────────────────────────────────────────────┤
│ 不可变借用 &T Fn 多次 &self │
│ 可变借用 &mut T FnMut 多次 &mut self │
│ 所有权 T FnOnce 一次 self │
├─────────────────────────────────────────────────────┤
│ 继承关系: Fn : FnMut : FnOnce │
│ (实现 Fn 的自动实现 FnMut 和 FnOnce) │
└─────────────────────────────────────────────────────┘
move 关键字
// 强制获取所有权
let s = String::from("hello");
let closure = move || println!("{}", s);
// println!("{}", s); // ❌ s 已移动到闭包
// 常用于线程
use std::thread;
let data = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("{:?}", data); // data 所有权移入线程
});
handle.join().unwrap();
闭包作为参数
// 泛型方式(推荐)
fn apply<F>(f: F) where F: Fn(i32) -> i32 {
println!("{}", f(10));
}
// 或简写
fn apply2<F: Fn(i32) -> i32>(f: F) {
println!("{}", f(10));
}
// 使用 impl Trait
fn apply3(f: impl Fn(i32) -> i32) {
println!("{}", f(10));
}
// 动态分发(有性能开销)(智能指针会讲)
fn apply_dyn(f: &dyn Fn(i32) -> i32) {
println!("{}", f(10));
}
// 调用
apply(|x| x * 2); // 20
apply(|x| x + 5); // 15
闭包作为返回值
// 必须用 impl 或 Box
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n // move 捕获 n
}
// 动态返回不同闭包 (智能指针会讲)
fn make_op(op: &str) -> Box<dyn Fn(i32, i32) -> i32> {
match op {
"add" => Box::new(|a, b| a + b),
"mul" => Box::new(|a, b| a * b),
_ => Box::new(|a, _| a),
}
}
let add5 = make_adder(5);
println!("{}", add5(10)); // 15
let mul = make_op("mul");
println!("{}", mul(3, 4)); // 12
迭代器(Iterator)
Iterator Trait
pub trait Iterator {
type Item; // 关联类型
fn next(&mut self) -> Option<Self::Item>;
// ... 大量默认方法
}
创建迭代器
let v = vec![1, 2, 3];
// 三种方式
let iter1 = v.iter(); // 不可变引用 &T
let iter2 = v.iter_mut(); // 可变引用 &mut T(需 mut v)
let iter3 = v.into_iter(); // 获取所有权 T
// 区别
for x in v.iter() { } // x: &i32, v 仍可用
for x in v.into_iter() { } // x: i32, v 被消耗
// 其他创建方式
let range = 0..10; // Range
let repeat = std::iter::repeat(1); // 无限重复
let once = std::iter::once(42); // 单元素
let empty: std::iter::Empty<i32> = std::iter::empty();
惰性求值
// 迭代器是惰性的,不消费不执行!
let v = vec![1, 2, 3, 4, 5];
let mapped = v.iter()
.map(|x| {
println!("mapping {}", x); // 不会打印!
x * 2
});
// 只有消费时才执行
let result: Vec<_> = mapped.collect(); // 现在打印
迭代器适配器(Adapter)
适配器返回新迭代器,惰性求值:
常用适配器
let v = vec![1, 2, 3, 4, 5, 6];
// map - 转换每个元素
v.iter().map(|x| x * 2); // [2, 4, 6, 8, 10, 12]
// filter - 过滤元素
v.iter().filter(|x| *x % 2 == 0); // [2, 4, 6]
// filter_map - 过滤 + 转换
v.iter().filter_map(|x| {
if x % 2 == 0 { Some(x * 10) } else { None }
}); // [20, 40, 60]
// take / skip
v.iter().take(3); // [1, 2, 3]
v.iter().skip(3); // [4, 5, 6]
// take_while / skip_while
v.iter().take_while(|x| **x < 4); // [1, 2, 3]
v.iter().skip_while(|x| **x < 4); // [4, 5, 6]
// enumerate - 带索引
v.iter().enumerate(); // [(0,1), (1,2), (2,3), ...]
// zip - 合并两个迭代器
let a = [1, 2, 3];
let b = ["a", "b", "c"];
a.iter().zip(b.iter()); // [(1,"a"), (2,"b"), (3,"c")]
// chain - 连接迭代器
a.iter().chain(b.iter()); // [1, 2, 3, "a", "b", "c"] (类型需相同)
// flatten - 展平嵌套
let nested = vec![vec![1, 2], vec![3, 4]];
nested.into_iter().flatten(); // [1, 2, 3, 4]
// flat_map - map + flatten
nested.into_iter().flat_map(|v| v.into_iter());
// rev - 反转(需 DoubleEndedIterator)
v.iter().rev(); // [6, 5, 4, 3, 2, 1]
// peekable - 可预览下一个
let mut iter = v.iter().peekable();
iter.peek(); // Some(&1),不消耗
iter.next(); // Some(&1),消耗
// cycle - 无限循环
v.iter().cycle(); // 1, 2, 3, 4, 5, 6, 1, 2, ...
// cloned / copied - 从引用创建值
v.iter().cloned(); // 等同于 .map(|x| x.clone())
v.iter().copied(); // 用于 Copy 类型
消费者(Consumer)
消费者消耗迭代器,产生最终结果:
let v = vec![1, 2, 3, 4, 5];
// collect - 收集到集合
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
let set: HashSet<_> = v.iter().collect();
let s: String = ['a', 'b', 'c'].iter().collect();
// sum / product - 求和/积
let total: i32 = v.iter().sum(); // 15
let product: i32 = v.iter().product(); // 120
// count - 计数
let count = v.iter().count(); // 5
// fold - 折叠(最灵活)
let sum = v.iter().fold(0, |acc, x| acc + x); // 15
let concat = v.iter().fold(String::new(), |mut acc, x| {
acc.push_str(&x.to_string());
acc
}); // "12345"
// reduce - 类似 fold,但用第一个元素作初始值
let max = v.iter().copied().reduce(|a, b| a.max(b)); // Some(5)
// for_each - 对每个元素执行操作
v.iter().for_each(|x| println!("{}", x));
// find / position - 查找
let found = v.iter().find(|&&x| x > 3); // Some(&4)
let pos = v.iter().position(|&x| x == 3); // Some(2)
// any / all - 存在/全部判断
let has_even = v.iter().any(|x| x % 2 == 0); // true
let all_pos = v.iter().all(|x| *x > 0); // true
// min / max
let min = v.iter().min(); // Some(&1)
let max = v.iter().max(); // Some(&5)
// min_by / max_by - 自定义比较
let max_by_abs = [-3, 1, 2].iter()
.max_by(|a, b| a.abs().cmp(&b.abs())); // Some(&-3)
// nth - 取第 n 个(消耗前面的)
let third = v.iter().nth(2); // Some(&3)
// last - 取最后一个
let last = v.iter().last(); // Some(&5)
// partition - 分割
let (even, odd): (Vec<_>, Vec<_>) = v.iter()
.partition(|x| *x % 2 == 0);
// even: [2, 4], odd: [1, 3, 5]
自定义迭代器
struct Counter {
current: u32,
max: u32,
}
impl Counter {
fn new(max: u32) -> Self {
Counter { current: 0, max }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.max {
self.current += 1;
Some(self.current)
} else {
None
}
}
}
// 使用
let counter = Counter::new(5);
let v: Vec<_> = counter.collect(); // [1, 2, 3, 4, 5]
// 获得所有适配器方法!
let sum: u32 = Counter::new(5)
.filter(|x| x % 2 == 0)
.map(|x| x * 10)
.sum(); // 60 (2*10 + 4*10)
性能:零成本抽象
// 这两种写法性能相同!
// 函数式
let sum: i32 = (1..=100)
.filter(|x| x % 2 == 0)
.map(|x| x * x)
.sum();
// 命令式
let mut sum = 0;
for x in 1..=100 {
if x % 2 == 0 {
sum += x * x;
}
}
// 编译器会将函数式版本优化成与命令式相同的机器码
何时迭代器更快
// 迭代器可以避免边界检查
let v = vec![1, 2, 3, 4, 5];
// 带边界检查
for i in 0..v.len() {
println!("{}", v[i]); // 每次访问都检查边界
}
// 无边界检查
for x in &v {
println!("{}", x); // 直接访问,更快
}
常见模式
错误处理收集
let strings = vec!["1", "2", "three", "4"];
// 收集所有结果(遇错即停)
let result: Result<Vec<i32>, _> = strings
.iter()
.map(|s| s.parse::<i32>())
.collect(); // Err(ParseIntError)
// 只收集成功的
let numbers: Vec<i32> = strings
.iter()
.filter_map(|s| s.parse().ok())
.collect(); // [1, 2, 4]
分组
use std::collections::HashMap;
let data = vec![("a", 1), ("b", 2), ("a", 3), ("b", 4)];
let grouped: HashMap<&str, Vec<i32>> = data
.into_iter()
.fold(HashMap::new(), |mut map, (k, v)| {
map.entry(k).or_default().push(v);
map
});
// {"a": [1, 3], "b": [2, 4]}
窗口/块处理
let v = vec![1, 2, 3, 4, 5];
// chunks - 固定大小块
for chunk in v.chunks(2) {
println!("{:?}", chunk); // [1,2], [3,4], [5]
}
// windows - 滑动窗口
for window in v.windows(3) {
println!("{:?}", window); // [1,2,3], [2,3,4], [3,4,5]
}
智能指针
┌────────────────────────────────────────────────────────────────────┐
│ 智能指针全景图 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Box<T> │ │ Rc<T> │ │ Arc<T> │ │RefCell<T>│ │
│ │ 堆分配 │ │ 引用计数 │ │原子引用 │ │内部可变 │ │
│ │ 单一所有 │ │ 共享所有 │ │线程安全 │ │运行时借用│ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Cell<T> │ │ Weak<T> │ │ Cow<T> │ │
│ │Copy可变 │ │ 弱引用 │ │写时复制 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 智能指针 = 数据 + 元数据 + 自动行为(Deref + Drop) │
└────────────────────────────────────────────────────────────────────┘
Box\<T> — 堆上分配
// Box 的简化内部实现
pub struct Box<
T: ?Sized, // 支持动态大小类型 (如 dyn Trait, [T])
A: Allocator = Global, // 分配器,默认为全局分配器
>(Unique<T>, A);
// Unique<T> 的定义 - 表示唯一所有权的指针
#[repr(transparent)]
pub struct Unique<T: ?Sized> {
pointer: NonNull<T>, // 非空指针
_marker: PhantomData<T>, // 拥有 T 的所有权(影响 drop 检查)
}
// NonNull<T> 的定义 - 非空指针
#[repr(transparent)]
pub struct NonNull<T: ?Sized> {
pointer: *const T, // 裸指针(但保证非空)
}
// Global 分配器(零大小类型)
pub struct Global;
fn main() {
// 1. 基本用法:在堆上分配数据
let b = Box::new(5);
println!("b = {}", b); // 自动解引用
// 2. 递归类型(编译时大小未知)
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
println!("{:?}", list);
// 3. trait 对象
trait Animal {
fn speak(&self);
}
struct Dog;
impl Animal for Dog {
fn speak(&self) { println!("Woof!"); }
}
let animal: Box<dyn Animal> = Box::new(Dog);
animal.speak();
// 4. 大数据避免栈溢出
let large_array: Box<[i32; 1000000]> = Box::new([0; 1000000]);
println!("Array length: {}", large_array.len());
}
Box 内存布局
栈 (Stack) 堆 (Heap)
┌─────────────────┐ ┌─────────────────┐
│ MyBox<T> │ │ │
│ ┌─────────────┐ │ │ T (value) │
│ │ ptr ────────┼─┼─────────►│ 42 │
│ └─────────────┘ │ │ │
│ ┌─────────────┐ │ └─────────────────┘
│ │ _marker │ │ size = size_of::<T>()
│ │ (0 bytes) │ │
│ └─────────────┘ │
└─────────────────┘
size = 8 bytes
(仅一个指针)
Rc\<T> — 引用计数
// Rc 的简化内部实现
pub struct Rc<
T: ?Sized, // 支持动态大小类型
A: Allocator = Global, // 分配器,默认全局
> {
ptr: NonNull<RcInner<T>>, // 指向堆上的 RcInner
phantom: PhantomData<RcInner<T>>, // 表示拥有 RcInner<T>
alloc: A, // 分配器实例
}
// RcInner (也叫 RcBox) 的定义 - 堆上的实际数据
#[repr(C)]
struct RcInner<T: ?Sized> {
strong: Cell<usize>, // 强引用计数
weak: Cell<usize>, // 弱引用计数
value: T, // 实际存储的数据
}
// Weak 的定义
pub struct Weak<
T: ?Sized,
A: Allocator = Global,
> {
ptr: NonNull<RcInner<T>>, // 指向同一个 RcInner
alloc: A,
}
use std::rc::Rc;
fn main() {
// 1. 基本共享所有权
let a = Rc::new(String::from("Hello"));
println!("引用计数: {}", Rc::strong_count(&a)); // 1
let b = Rc::clone(&a); // 增加引用计数,不是深拷贝
println!("引用计数: {}", Rc::strong_count(&a)); // 2
{
let c = Rc::clone(&a);
println!("引用计数: {}", Rc::strong_count(&a)); // 3
} // c 离开作用域
println!("引用计数: {}", Rc::strong_count(&a)); // 2
// 2. 共享图结构
#[derive(Debug)]
struct Node {
value: i32,
children: Vec<Rc<Node>>,
}
let leaf = Rc::new(Node { value: 3, children: vec![] });
let branch1 = Rc::new(Node {
value: 1,
children: vec![Rc::clone(&leaf)],
});
let branch2 = Rc::new(Node {
value: 2,
children: vec![Rc::clone(&leaf)], // leaf 被两个节点共享
});
println!("leaf 引用计数: {}", Rc::strong_count(&leaf)); // 3
}
Rc 内存布局
栈 堆
┌──────┐ ┌─────────────────┐
│ a │────────────────►│ RcBox<T> │
├──────┤ │ ┌─────────────┐ │
│ b │────────────────►│ │strong: 3 │ │
├──────┤ │ │weak: 1 │ │
│ c │────────────────►│ │data: "Hello"│ │
└──────┘ │ └─────────────┘ │
└─────────────────┘
Rc<T> 使用默认 Global 分配器时:
栈上 (Stack) 堆上 (Heap)
┌────────────────────────────────────┐ ┌────────────────────────────────┐
│ Rc<T, Global> │ │ RcInner<T> │
│ ┌──────────────────────────────┐ │ │ ┌──────────────────────────┐ │
│ │ ptr: NonNull<RcInner<T>> │ │ │ │ strong: Cell<usize> │ │
│ │ └── pointer: *const 8B │──┼────────►│ │ value: usize 8B │ │
│ ├──────────────────────────────┤ │ │ ├──────────────────────────┤ │
│ │ phantom: PhantomData 0B │ │ │ │ weak: Cell<usize> │ │
│ ├──────────────────────────────┤ │ │ │ value: usize 8B │ │
│ │ alloc: Global 0B │ │ │ ├──────────────────────────┤ │
│ └──────────────────────────────┘ │ │ │ value: T │ │
├────────────────────────────────────┤ │ │ (实际数据) │ │
│ Total: 8 bytes │ │ └──────────────────────────┘ │
└────────────────────────────────────┘ │ Total: 16 + size_of::<T>() │
└────────────────────────────────┘
Arc\<T> — 原子引用计数
pub struct Arc<
T: ?Sized, // 支持动态大小类型
A: Allocator = Global, // 分配器,默认全局
> {
ptr: NonNull<ArcInner<T>>, // 指向堆上的 ArcInner
phantom: PhantomData<ArcInner<T>>, // 表示拥有 ArcInner<T>
alloc: A, // 分配器实例
}
// ArcInner 的定义 - 堆上的实际数据
#[repr(C)]
struct ArcInner<T: ?Sized> {
strong: AtomicUsize, // 原子强引用计数 ← 关键区别!
weak: AtomicUsize, // 原子弱引用计数
value: T, // 实际存储的数据
}
// Weak 的定义
pub struct Weak<
T: ?Sized,
A: Allocator = Global,
> {
ptr: NonNull<ArcInner<T>>, // 指向同一个 ArcInner
alloc: A,
}
use std::sync::Arc;
use std::thread;
fn main() {
// 1. 跨线程共享数据
let data = Arc::new(vec![1, 2, 3, 4, 5]);
let mut handles = vec![];
for i in 0..3 {
let data_clone = Arc::clone(&data);
let handle = thread::spawn(move || {
println!("线程 {}: {:?}", i, data_clone);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("最终引用计数: {}", Arc::strong_count(&data));
// 2. 配合 Mutex 实现可变共享
use std::sync::Mutex;
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("计数器: {}", *counter.lock().unwrap()); // 10
}
Arc 内存布局
Arc<T> 使用默认 Global 分配器时:
栈上 (Stack) 堆上 (Heap)
┌────────────────────────────────────┐ ┌────────────────────────────────┐
│ Arc<T, Global> │ │ ArcInner<T> │
│ ┌──────────────────────────────┐ │ │ ┌──────────────────────────┐ │
│ │ ptr: NonNull<ArcInner<T>> │ │ │ │ strong: AtomicUsize │ │
│ │ └── pointer: *const 8B │──┼────────►│ │ value: usize 8B │ │
│ ├──────────────────────────────┤ │ │ ├──────────────────────────┤ │
│ │ phantom: PhantomData 0B │ │ │ │ weak: AtomicUsize │ │
│ ├──────────────────────────────┤ │ │ │ value: usize 8B │ │
│ │ alloc: Global 0B │ │ │ ├──────────────────────────┤ │
│ └──────────────────────────────┘ │ │ │ value: T │ │
├────────────────────────────────────┤ │ │ (实际数据) │ │
│ Total: 8 bytes │ │ └──────────────────────────┘ │
└────────────────────────────────────┘ │ Total: 16 + size_of::<T>() │
└────────────────────────────────┘
注: AtomicUsize 和 usize 大小相同,都是 8 bytes (64位系统)
原子性是通过 CPU 指令保证的,不需要额外空间
Arc<T, A>
│
├── ptr: NonNull<ArcInner<T>>
│ │
│ └── *const ArcInner<T> ─────────────────────┐
│ │
├── phantom: PhantomData<ArcInner<T>> │
│ └── 零大小,表示逻辑上拥有 ArcInner<T> │
│ │
└── alloc: A │
└── 默认 Global (零大小) │
▼
┌──────────────────────────┐
│ ArcInner<T> (堆上) │
Weak<T, A> │ │
│ │ strong: AtomicUsize ◄────┼── 原子操作
├── ptr: NonNull<ArcInner<T>> ──────────►│ weak: AtomicUsize ◄────┼── 原子操作
│ │ value: T │
└── alloc: A └──────────────────────────┘
所有权与线程安全
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ Arc<T> ──────► ArcInner<T> ──────► T │
│ │ │ │ │
│ │ │ └── 多线程共享只读访问 (&T) │
│ │ │ │
│ │ └── 引用计数通过原子操作保护 │
│ │ │
│ └── 可以 Send 到其他线程 (当 T: Send + Sync) │
│ │
│ 要修改 T,需要: │
│ - Arc<Mutex<T>> 或 Arc<RwLock<T>> │
│ - Arc::make_mut (会克隆) │
│ - Arc::get_mut (需要唯一引用) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
多线程共享示例
Thread 1 Thread 2 Thread 3
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ arc1: Arc │ │ arc2: Arc │ │ arc3: Arc │
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │
│ │ ptr ─────┼─┼───┐ │ │ ptr ─────┼─┼───┐ │ │ ptr ─────┼─┼───┐
│ └──────────┘ │ │ │ └──────────┘ │ │ │ └──────────┘ │ │
└──────────────┘ │ └──────────────┘ │ └──────────────┘ │
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ ArcInner<i32> (堆上,共享) │
│ ┌───────────────────────────────────────────────┐ │
│ │ strong: AtomicUsize = 3 │ │
│ │ ▲ │ │
│ │ │ 原子操作: fetch_add / fetch_sub │ │
│ │ │ 多线程同时修改也安全 │ │
│ ├───────────────────────────────────────────────┤ │
│ │ weak: AtomicUsize = 1 │ │
│ ├───────────────────────────────────────────────┤ │
│ │ value: i32 = 42 │ │
│ └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Rc vs Arc 对比
┌─────────────────────────────────────────────────────────────────────────────┐
│ Rc vs Arc 内部结构对比 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ RcInner<T> ArcInner<T> │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ strong: Cell<usize> │ │ strong: AtomicUsize │ │
│ │ 普通读写 │ │ 原子操作 │ │
│ │ cell.get() / set() │ │ atomic.load() / fetch_sub() │
│ ├─────────────────────────┤ ├─────────────────────────┤ │
│ │ weak: Cell<usize> │ │ weak: AtomicUsize │ │
│ │ 普通读写 │ │ 原子操作 │ │
│ ├─────────────────────────┤ ├─────────────────────────┤ │
│ │ value: T │ │ value: T │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ❌ !Send, !Sync ✅ Send + Sync (当 T: Send + Sync) │
│ ✅ 更快 (无同步开销) ❌ 有原子操作开销 │
│ 📍 单线程使用 📍 多线程共享 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
RefCell\<T> — 内部可变性
// 内部简化数据结构
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>, // 借用状态计数器
#[cfg(feature = "debug_refcell")]
borrowed_at: Cell<Option<&'static Location<'static>>>, // 调试用:借用位置
value: UnsafeCell<T>, // 实际存储的值
}
// 借用标志类型 (实际是 isize)
type BorrowFlag = isize;
const UNUSED: BorrowFlag = 0; // 未借用
const WRITING: BorrowFlag = -1; // 可变借用中 (只能有一个)
// 正数表示不可变借用的数量
// Ref<T> - 不可变借用的守卫
pub struct Ref<'b, T: ?Sized + 'b> {
value: NonNull<T>, // 指向数据的指针
borrow: BorrowRef<'b>, // 借用计数的引用
}
struct BorrowRef<'b> {
borrow: &'b Cell<BorrowFlag>, // 指向 RefCell.borrow 的引用
}
// RefMut<T> - 可变借用的守卫
pub struct RefMut<'b, T: ?Sized + 'b> {
value: NonNull<T>, // 指向数据的指针
borrow: BorrowRefMut<'b>, // 可变借用计数的引用
#[cfg(feature = "debug_refcell")]
marker: PhantomData<&'b mut T>,
}
struct BorrowRefMut<'b> {
borrow: &'b Cell<BorrowFlag>, // 指向 RefCell.borrow 的引用
}
use std::cell::RefCell;
fn main() {
// 1. 基本用法:运行时借用检查
let data = RefCell::new(5);
// 获取可变借用
*data.borrow_mut() += 1;
println!("data = {}", data.borrow()); // 6
// 2. 不可变结构中的可变字段
struct Cache {
data: RefCell<Option<String>>,
}
impl Cache {
fn new() -> Self {
Cache { data: RefCell::new(None) }
}
// &self 但可以修改内部数据
fn get(&self) -> String {
let mut cache = self.data.borrow_mut();
if cache.is_none() {
*cache = Some("computed value".to_string());
}
cache.clone().unwrap()
}
}
let cache = Cache::new();
println!("{}", cache.get()); // 计算并缓存
println!("{}", cache.get()); // 直接返回缓存
// 3. ⚠️ 运行时借用规则检查
let value = RefCell::new(10);
let _borrow1 = value.borrow();
let _borrow2 = value.borrow(); // ✅ 多个不可变借用
// let _borrow3 = value.borrow_mut(); // ❌ panic! 已有不可变借用
}
RefCell 借用规则
use std::cell::RefCell;
fn demonstrate_refcell_rules() {
let cell = RefCell::new(42);
// ✅ 多个不可变借用
{
let r1 = cell.borrow();
let r2 = cell.borrow();
println!("{} {}", r1, r2);
}
// ✅ 单个可变借用
{
let mut w = cell.borrow_mut();
*w += 1;
}
// ❌ 不可变和可变同时存在 -> panic!
// {
// let r = cell.borrow();
// let w = cell.borrow_mut(); // panic at runtime!
// }
// 安全检查方法
if let Ok(r) = cell.try_borrow() {
println!("借用成功: {}", r);
}
if let Ok(w) = cell.try_borrow_mut() {
println!("可变借用成功");
}
}
RefCell 内存布局
RefCell<T> 布局 (release 模式):
┌─────────────────────────────────────────────────────────────┐
│ RefCell<T> │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ borrow: Cell<BorrowFlag> │ │
│ │ └── value: UnsafeCell<isize> 8B │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ value: UnsafeCell<T> │ │
│ │ └── value: T size_of<T> │ │
│ └───────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Total: 8 + size_of::<T>() (+ 对齐填充) │
└─────────────────────────────────────────────────────────────┘
RefCell<i32> 具体布局:
┌──────────────────────────────────────┐
│ offset 0: borrow: isize 8B │ 借用计数
├──────────────────────────────────────┤
│ offset 8: value: i32 4B │ 实际数据
├──────────────────────────────────────┤
│ offset 12: padding 4B │ 对齐填充
├──────────────────────────────────────┤
│ Total: 16 bytes │
└──────────────────────────────────────┘
线程安全性
┌─────────────────────────────────────────────────────────────────────────────┐
│ RefCell 不是线程安全的! │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ RefCell<T>: !Sync │
│ │
│ 原因: borrow 计数使用 Cell<isize>,不是原子操作 │
│ │
│ Thread 1 Thread 2 │
│ ───────── ───────── │
│ b = borrow.get() // 0 │
│ b = borrow.get() // 0 │
│ borrow.set(b-1) // -1 │
│ borrow.set(b-1) // -1 ← 两个都认为自己成功了! │
│ 写入数据... │
│ 写入数据... ← 数据竞争!UB! │
│ │
│ 解决方案: │
│ - 单线程: 使用 RefCell │
│ - 多线程: 使用 Mutex 或 RwLock │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
对比表:
┌────────────────┬─────────────┬─────────────┬─────────────────────────────────┐
│ │ Send │ Sync │ 说明 │
├────────────────┼─────────────┼─────────────┼─────────────────────────────────┤
│ Cell<T> │ ✅ * │ ❌ │ 不能共享引用到其他线程 │
├────────────────┼─────────────┼─────────────┼─────────────────────────────────┤
│ RefCell<T> │ ✅ * │ ❌ │ 不能共享引用到其他线程 │
├────────────────┼─────────────┼─────────────┼─────────────────────────────────┤
│ Mutex<T> │ ✅ * │ ✅ * │ 原子锁,可以跨线程共享 │
├────────────────┼─────────────┼─────────────┼─────────────────────────────────┤
│ RwLock<T> │ ✅ * │ ✅ * │ 原子锁,可以跨线程共享 │
└────────────────┴─────────────┴─────────────┴─────────────────────────────────┘
* 当 T: Send 时
Cell\<T> — Copy 类型的内部可变性
pub struct Cell<T: ?Sized> {
value: UnsafeCell<T>, // 就这一个字段,没有任何额外开销
}
// UnsafeCell - 内部可变性的根基
#[repr(transparent)]
pub struct UnsafeCell<T: ?Sized> {
value: T,
}
use std::cell::Cell;
fn main() {
// 1. 基本用法:适用于 Copy 类型
let cell = Cell::new(5);
cell.set(10); // 设置新值
let value = cell.get(); // 获取值的副本
println!("value = {}", value);
// 2. 计数器示例
struct Counter {
count: Cell<u32>,
}
impl Counter {
fn new() -> Self {
Counter { count: Cell::new(0) }
}
fn increment(&self) { // 注意:&self 而非 &mut self
self.count.set(self.count.get() + 1);
}
fn get(&self) -> u32 {
self.count.get()
}
}
let counter = Counter::new();
counter.increment();
counter.increment();
println!("count = {}", counter.get()); // 2
// 3. Cell vs RefCell
// Cell: 更轻量,只适用于 Copy 类型,无运行时借用检查
// RefCell: 适用于任何类型,有运行时借用检查开销
}
Cell vs RefCell 对比
Cell<T> 是零开销抽象!
┌─────────────────────────────────────────────────────────────────────────────┐
│ Cell<T> 内存布局 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Cell<i32>: 普通 i32: │
│ ┌───────────────────┐ ┌───────────────────┐ │
│ │ value: i32 4B │ === │ value: i32 4B │ │
│ └───────────────────┘ └───────────────────┘ │
│ Total: 4B Total: 4B │
│ │
│ Cell<u8>: │
│ ┌───────────────────┐ │
│ │ value: u8 1B │ size_of::<Cell<T>>() == size_of::<T>() │
│ └───────────────────┘ align_of::<Cell<T>>() == align_of::<T>() │
│ Total: 1B │
│ │
│ Cell<[i32; 4]>: │
│ ┌───────────────────┐ │
│ │ [i32; 4] 16B │ │
│ └───────────────────┘ │
│ Total: 16B │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
对比 RefCell<T>:
┌──────────────────────────────────────────────────────┐
│ RefCell<i32>: │
│ ┌──────────────────────────────────────────────┐ │
│ │ borrow: Cell<isize> 8B │ │ ← 额外开销!
│ ├──────────────────────────────────────────────┤ │
│ │ value: UnsafeCell<i32> 4B │ │
│ ├──────────────────────────────────────────────┤ │
│ │ padding 4B │ │
│ └──────────────────────────────────────────────┘ │
│ Total: 16B (Cell<i32> 的 4 倍!) │
└──────────────────────────────────────────────────────┘
Weak\<T> — 弱引用
pub struct Weak<
T: ?Sized,
A: Allocator = Global,
> {
// 这是 NonNull 以便在 enum 中优化大小
// 但它不一定是有效指针!
// Weak::new() 将其设为 usize::MAX,这样不需要堆分配
// 这不会与真实指针冲突,因为 RcInner 至少有 2 字节对齐
ptr: NonNull<RcInner<T>>,
alloc: A,
}
// RcInner 的定义(与 Rc 共享)
struct RcInner<T: ?Sized> {
strong: Cell<usize>, // 强引用计数
weak: Cell<usize>, // 弱引用计数
value: T, // 实际数据
}
// 特殊的"空"Weak 标记
// RcInner 的对齐至少为 2,所以 usize::MAX 永远不是有效地址
const WEAK_EMPTY: usize = usize::MAX;
use std::rc::{Rc, Weak};
use std::cell::RefCell;
// 树结构:父子双向引用
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>, // 弱引用父节点
children: RefCell<Vec<Rc<Node>>>, // 强引用子节点
}
fn main() {
// 创建树结构
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!("leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf));
{
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
// 设置 leaf 的父节点(弱引用)
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("branch strong = {}, weak = {}",
Rc::strong_count(&branch),
Rc::weak_count(&branch));
// 通过弱引用访问父节点
if let Some(parent) = leaf.parent.borrow().upgrade() {
println!("leaf's parent value = {}", parent.value);
}
} // branch 离开作用域被释放
// branch 已释放,弱引用升级失败
println!("leaf's parent = {:?}", leaf.parent.borrow().upgrade()); // None
}
weak 内存布局
Weak<T> 有两种状态:
状态 1: 空的 Weak(Weak::new() 创建)
┌─────────────────────────────────────────────────────────────────────────────┐
│ Weak<T, Global> 栈上布局 │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ ptr: NonNull<RcInner<T>> │ │
│ │ └── pointer = 0xFFFF_FFFF_FFFF_FFFF (usize::MAX) 8B │ │
│ │ 这是一个标记值,表示"没有指向任何东西" │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ alloc: Global 0B │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ Total: 8 bytes │
│ │
│ 堆上: 无!不需要分配任何内存 │
└─────────────────────────────────────────────────────────────────────────────┘
状态 2: 指向 RcInner 的 Weak(从 Rc::downgrade 创建)
┌─────────────────────────────────────────────────────────────────────────────┐
│ Weak<T, Global> 栈上布局 │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ ptr: NonNull<RcInner<T>> │ │
│ │ └── pointer = 0x7fff_xxxx_xxxx (有效堆地址) 8B │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ alloc: Global 0B │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ Total: 8 bytes │
│ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ RcInner<T> (堆上,与 Rc 共享) │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ strong: Cell<usize> 8B │ │ │
│ │ ├─────────────────────────────┤ │ │
│ │ │ weak: Cell<usize> 8B │ │ │
│ │ ├─────────────────────────────┤ │ │
│ │ │ value: T │ │ ← 可能已被 drop! │
│ │ └─────────────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Rc 和 Weak 共享同一个 RcInner │
├─────────────────────────────────────────────────────────────────────────────┤
Rc<T> Weak<T> Weak<T>
┌──────────┐ ┌──────────┐ ┌──────────┐
│ ptr ─────┼─────┐ │ ptr ─────┼─────┐ │ ptr ─────┼─────┐
│ phantom │ │ │ alloc │ │ │ alloc │ │
│ alloc │ │ └──────────┘ │ └──────────┘ │
└──────────┘ │ │ │
│ │ │
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ RcInner<T> (堆上) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ strong: Cell<usize> = 1 │ │
│ │ ▲ │ │
│ │ │ Rc 数量 │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ weak: Cell<usize> = 3 │ │
│ │ ▲ │ │
│ │ │ Weak 数量 + 1(有 Rc 时额外 +1) │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ value: T │ │
│ │ ▲ │ │
│ │ │ 当 strong → 0 时被 drop │ │
│ │ │ 但 RcInner 还在(因为 Weak 存在) │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
当 strong = 0 且 weak = 1 时释放 RcInner
└─────────────────────────────────────────────────────────────────────────────┘
Cow\<T> — 写时复制
// Cow = Clone on Write(写时复制)
pub enum Cow<'a, B: ?Sized + 'a>
where
B: ToOwned,
{
/// 借用的数据
Borrowed(&'a B),
/// 拥有的数据
Owned(<B as ToOwned>::Owned),
}
// ToOwned trait - 定义借用类型和拥有类型的关系
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
fn clone_into(&self, target: &mut Self::Owned) { ... }
}
use std::borrow::Cow;
fn main() {
// 1. 基本用法
let s1: Cow<str> = Cow::Borrowed("hello"); // 借用
let s2: Cow<str> = Cow::Owned(String::from("world")); // 拥有
println!("{} {}", s1, s2);
// 2. 实际应用:条件修改
fn process_text(text: &str) -> Cow<str> {
if text.contains("bad") {
// 需要修改时才分配新内存
Cow::Owned(text.replace("bad", "good"))
} else {
// 不需要修改,零成本借用
Cow::Borrowed(text)
}
}
let text1 = "this is good";
let text2 = "this is bad";
let result1 = process_text(text1); // Borrowed,无分配
let result2 = process_text(text2); // Owned,有分配
println!("{}", result1);
println!("{}", result2);
// 3. 转为可变时自动克隆
let mut cow: Cow<str> = Cow::Borrowed("hello");
// to_mut() 会在必要时克隆
cow.to_mut().push_str(" world");
println!("{}", cow); // "hello world"
// 4. 函数参数灵活性
fn print_cow<'a>(s: impl Into<Cow<'a, str>>) {
let cow: Cow<str> = s.into();
println!("{}", cow);
}
print_cow("borrowed str"); // &str
print_cow(String::from("owned")); // String
}
Cow 内存布局
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ Cow::Borrowed(&str): │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ [0..8] ptr: 指向字符串数据 8B │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ [8..16] len: 字符串长度 8B │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ [16..24] marker: usize::MAX (0xFFFF_FFFF_FFFF_FFFF) 8B │ │
│ │ ▲ │ │
│ │ └── 这个特殊值表示 "我是 Borrowed" │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ │
│ Cow::Owned(String): │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ [0..8] ptr: 指向堆上数据 8B │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ [8..16] len: 字符串长度 8B │ │
│ ├───────────────────────────────────────────────────────────────────────┤ │
│ │ [16..24] cap: 分配的容量 (永远 < usize::MAX) 8B │ │
│ │ ▲ │ │
│ │ └── 真实的容量值,不会是 MAX │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
判断逻辑:
┌────────────────────────────────────────────────────────────────┐
│ │
│ fn is_borrowed(cow: &Cow<str>) -> bool { │
│ // 伪代码,展示内部判断逻辑 │
│ cow.cap_or_marker == usize::MAX │
│ } │
│ │
│ if cap_or_marker == usize::MAX { │
│ // 是 Borrowed,ptr 和 len 构成 &str │
│ } else { │
│ // 是 Owned,ptr, len, cap 构成 String │
│ } │
│ │
└────────────────────────────────────────────────────────────────┘
引用计数与数据释放
Rust 中用到引用计数的类型
┌─────────────────────────────────────────────────────────────────────────────┐
│ Rust 引用计数类型 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 类型 │ 模块 │ 适用场景 │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ Rc<T> │ std::rc │ 单线程共享所有权 │ │
│ │ Weak<T> │ std::rc │ Rc 的弱引用 │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ Arc<T> │ std::sync │ 多线程共享所有权 │ │
│ │ Weak<T> │ std::sync │ Arc 的弱引用 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Rc = Reference Counted │
│ Arc = Atomically Reference Counted │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
内存布局
┌─────────────────────────────────────────────────────────────────────────────┐
│ Rc<T> / Arc<T> 内存结构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 栈上 堆上 │
│ ┌─────────┐ ┌──────────────────────────────┐ │
│ │ Rc<T> │ │ RcBox<T> │ │
│ │ │ ├──────────────────────────────┤ │
│ │ ptr ───┼───────────────►│ strong: Cell<usize> │ ← 强引用计数 │
│ │ │ │ weak: Cell<usize> │ ← 弱引用计数 │
│ └─────────┘ ├──────────────────────────────┤ │
│ │ │ │
│ ┌─────────┐ │ value: T │ ← 实际数据 │
│ │ Weak<T> │ │ │ │
│ │ │ │ │ │
│ │ ptr ───┼───────────────►└──────────────────────────────┘ │
│ │ │ │
│ └─────────┘ │
│ │
│ Arc<T> 结构相同,但计数器使用 AtomicUsize(原子操作) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
计数变化规则
use std::rc::{Rc, Weak};
fn main() {
// ═══════════════════════════════════════════════════
// 创建:strong=1, weak=0
// ═══════════════════════════════════════════════════
let a = Rc::new(String::from("hello"));
println!("创建后: strong={}, weak={}",
Rc::strong_count(&a), Rc::weak_count(&a));
// 输出: strong=1, weak=0
// ═══════════════════════════════════════════════════
// Rc::clone(): strong += 1
// ═══════════════════════════════════════════════════
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("clone 两次: strong={}, weak={}",
Rc::strong_count(&a), Rc::weak_count(&a));
// 输出: strong=3, weak=0
// ═══════════════════════════════════════════════════
// Rc::downgrade(): weak += 1
// ═══════════════════════════════════════════════════
let w1: Weak<String> = Rc::downgrade(&a);
let w2: Weak<String> = Rc::downgrade(&a);
println!("downgrade 两次: strong={}, weak={}",
Rc::strong_count(&a), Rc::weak_count(&a));
// 输出: strong=3, weak=2
// ═══════════════════════════════════════════════════
// drop(Rc): strong -= 1
// ═══════════════════════════════════════════════════
drop(b);
drop(c);
println!("drop 两个 Rc: strong={}, weak={}",
Rc::strong_count(&a), Rc::weak_count(&a));
// 输出: strong=1, weak=2
// ═══════════════════════════════════════════════════
// drop(Weak): weak -= 1
// ═══════════════════════════════════════════════════
drop(w1);
println!("drop 一个 Weak: strong={}, weak={}",
Rc::strong_count(&a), Rc::weak_count(&a));
// 输出: strong=1, weak=1
}
释放时机
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据释放时机 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ strong_count → 0 ══════► value: T 被 drop(析构函数执行) │ │
│ │ │ │
│ │ weak_count → 0 ══════► 整个控制块被释放(需 strong 也为 0) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ │
│ 状态流转: │
│ │
│ s>0, w≥0 s=0, w>0 s=0, w=0 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 数据存活 │ ───► │ 数据已释放│ ───► │ 全部释放 │ │
│ │ 控制块在 │ │ 控制块在 │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │
│ │ └── Weak.upgrade() 返回 None │
│ └── 可正常访问数据 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
释放过程演示
use std::rc::{Rc, Weak};
struct Data {
value: i32,
}
impl Drop for Data {
fn drop(&mut self) {
println!(" 💥 Data({}) 被析构", self.value);
}
}
fn main() {
let weak: Weak<Data>;
{
let rc1 = Rc::new(Data { value: 42 });
let rc2 = Rc::clone(&rc1);
weak = Rc::downgrade(&rc1);
println!("作用域内: strong={}, weak={}",
Rc::strong_count(&rc1), Rc::weak_count(&rc1));
// 输出: strong=2, weak=1
// rc1, rc2 离开作用域
}
// 输出: 💥 Data(42) 被析构
// strong 变为 0,数据被释放
// weak 还在,但数据已经没了
println!("作用域外: weak.strong_count()={}", weak.strong_count());
// 输出: weak.strong_count()=0
match weak.upgrade() {
Some(rc) => println!("数据: {}", rc.value),
None => println!("upgrade 失败,数据已释放"),
}
// 输出: upgrade 失败,数据已释放
}
stong=0,weak>0
┌─────────────────────────────────────────────────────────────────────────────┐
│ 内存状态对比 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ strong > 0 时 strong = 0, weak > 0 时 │
│ │
│ Weak ───► ┌─────────────────┐ Weak ───► ┌─────────────────┐ │
│ │ strong: 1 │ │ strong: 0 │ │
│ │ weak: 1 │ │ weak: 1 │ │
│ ├─────────────────┤ ├─────────────────┤ │
│ │ │ │ │ │
│ │ Data(42) ✅ │ │ (已释放) ❌ │ │
│ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │
│ upgrade(): upgrade(): │ │
│ 1. 读 strong → 1 1. 读 strong → 0 │ 控制块还在 │
│ 2. strong > 0 2. strong == 0 │ 所以能读取 │
│ 3. 返回 Some(Rc) 3. 返回 None │ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ 谁负责释放什么 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 操作 │ 行为 │
│ ────────────────────┼─────────────────────────────────────────────────── │
│ drop(Rc) │ strong-- │
│ │ 若 strong→0:释放数据 T │
│ │ 若 strong→0 且 weak=0:释放控制块 │
│ ────────────────────┼─────────────────────────────────────────────────── │
│ drop(Weak) │ weak-- │
│ │ 若 strong=0 且 weak→0:释放控制块 │
│ ────────────────────┼─────────────────────────────────────────────────── │
│ weak.upgrade() │ 只读取 strong_count,返回 Option │
│ │ 不修改计数,不释放任何东西 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
strong = 0, weak > 0时,控制块的存在意义就是为了 weak 不访问也指针。
智能指针对比
┌─────────────┬──────────┬──────────┬──────────┬─────────────────────┐
│ 智能指针 │ 所有权 │ 可变性 │ 线程安全 │ 主要用途 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Box<T> │ 独占 │ 继承 │ ✅ │ 堆分配、递归类型 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Rc<T> │ 共享 │ 不可变 │ ❌ │ 单线程共享所有权 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Arc<T> │ 共享 │ 不可变 │ ✅ │ 多线程共享所有权 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ RefCell<T> │ 独占 │内部可变 │ ❌ │ 运行时借用检查 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Cell<T> │ 独占 │内部可变 │ ❌ │ Copy类型内部可变 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Weak<T> │ 无 │ 不可变 │ 同Rc/Arc │ 打破循环引用 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Cow<T> │ 按需 │ 按需 │ ✅ │ 写时复制优化 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ Mutex<T> │ 共享 │内部可变 │ ✅ │ 多线程互斥访问 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ RwLock<T> │ 共享 │内部可变 │ ✅ │ 多线程读写锁 │
└─────────────┴──────────┴──────────┴──────────┴─────────────────────┘
选择决策流程
需要智能指针?
│
├─► 需要堆分配? ──► Box<T>
│
├─► 需要共享所有权?
│ │
│ ├─► 单线程 ──► Rc<T>
│ │ │
│ │ └─► 需要可变? ──► Rc<RefCell<T>>
│ │
│ └─► 多线程 ──► Arc<T>
│ │
│ └─► 需要可变? ──► Arc<Mutex<T>> 或 Arc<RwLock<T>>
│
├─► 需要内部可变性?
│ │
│ ├─► T: Copy ──► Cell<T>
│ │
│ └─► 其他 ──► RefCell<T>
│
└─► 需要避免克隆? ──► Cow<T>
循环引用 (Cyclic References)
问题本质
┌─────────────────────────────────────────────────────────────────────────────┐
│ 使用 Rc (循环引用 - 泄漏!) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 栈变量 a 栈变量 b │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Rc<RefCell<Node>>│ │ Rc<RefCell<Node>>│ │
│ │ strong_count: 2 │ │ strong_count: 2 │ ← 都是 2! │
│ │ weak_count: 0 │ │ weak_count: 0 │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ Node { │ │ Node { │ │
│ │ value: 1 │ │ value: 2 │ │
│ │ next: Some ───┼── Rc ───►│ next: Some ───┼── Rc ──┐ │
│ │ } │ │ } │ │ │
│ └──────────────────┘ └──────────────────┘ │ │
│ ▲ │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ 离开作用域后: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ a 析构: strong_count 2 → 1 (b.next 还持有) │ │
│ │ b 析构: strong_count 2 → 1 (a.next 还持有) │ │
│ │ 两者都不为 0,永远无法 drop! 💥 内存泄漏 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Rc 导致的内存泄漏
use std::cell::RefCell;
use std::rc::Rc;
struct Node {
value: i32,
next: Option<Rc<RefCell<Node>>>,
}
impl Drop for Node {
fn drop(&mut self) {
println!("🗑️ Dropping Node with value: {}", self.value);
}
}
fn main() {
println!("=== 创建循环引用 ===");
{
let a = Rc::new(RefCell::new(Node { value: 1, next: None }));
let b = Rc::new(RefCell::new(Node { value: 2, next: None }));
println!("a strong={}, b strong={} a weak={}, b weak={}",
Rc::strong_count(&a),
Rc::strong_count(&b),
Rc::weak_count(&a),
Rc::weak_count(&b));
a.borrow_mut().next = Some(Rc::clone(&b));
println!("a strong={}, b strong={} a weak={}, b weak={}",
Rc::strong_count(&a),
Rc::strong_count(&b),
Rc::weak_count(&a),
Rc::weak_count(&b));
b.borrow_mut().next = Some(Rc::clone(&a)); // 循环!
println!("a strong={}, b strong={} a weak={}, b weak={}",
Rc::strong_count(&a),
Rc::strong_count(&b),
Rc::weak_count(&a),
Rc::weak_count(&b));
}
println!("=== 离开作用域 ===");
println!("(如果没看到 Drop 消息,说明泄漏了!)");
}
解决方案:Weak
use std::cell::RefCell;
use std::rc::{Rc, Weak};
struct Node {
value: i32,
next: Option<Weak<RefCell<Node>>>, // 改用 Weak
}
impl Drop for Node {
fn drop(&mut self) {
println!("🗑️ Dropping Node with value: {}", self.value);
}
}
fn main() {
println!("=== 使用 Weak 打破循环 ===");
{
let a = Rc::new(RefCell::new(Node { value: 1, next: None }));
let b = Rc::new(RefCell::new(Node { value: 2, next: None }));
println!("a strong={}, a weak={}\nb strong={}, b weak={}",
Rc::strong_count(&a),
Rc::weak_count(&a),
Rc::strong_count(&b),
Rc::weak_count(&b));
a.borrow_mut().next = Some(Rc::downgrade(&b));
println!("a strong={}, a weak={}\nb strong={}, b weak={}",
Rc::strong_count(&a),
Rc::weak_count(&a),
Rc::strong_count(&b),
Rc::weak_count(&b));
b.borrow_mut().next = Some(Rc::downgrade(&a));
println!("a strong={}, a weak={}\nb strong={}, b weak={}",
Rc::strong_count(&a),
Rc::weak_count(&a),
Rc::strong_count(&b),
Rc::weak_count(&b));
}
println!("=== 离开作用域 ===");
}
┌─────────────────────────────────────────────────────────────────────────────┐
│ 使用 Weak (打破循环 - 正常释放) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 栈变量 a 栈变量 b │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Rc<RefCell<Node>>│ │ Rc<RefCell<Node>>│ │
│ │ strong_count: 1 │ │ strong_count: 1 │ ← 都是 1! │
│ │ weak_count: 1 │ │ weak_count: 1 │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ Node { │ │ Node { │ │
│ │ value: 1 │ │ value: 2 │ │
│ │ next: Some ───┼─ Weak ─ ─│─ ─next: Some ───┼─ Weak ─ ┐ │
│ │ } │ 弱! │ } │ 弱! │ │
│ └──────────────────┘ └──────────────────┘ │ │
│ ▲ │ │
│ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │
│ (虚线 = Weak,不增加 strong_count) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
完整的 Weak 方案析构过程
┌─────────────────────────────────────────────────────────────────────────────┐
│ Weak 方案的完整析构过程 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 初始状态: │
│ ══════════ │
│ a b │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ Weak ┌─────────────────┐ │
│ │ 控制块 │ │ 控制块 │ │
│ │ strong: 1 │◄─ ─ ─ ─ ─ ─ ─ ─│ strong: 1 │ │
│ │ weak: 1 │ │ weak: 1 │ │
│ ├─────────────────┤ ├─────────────────┤ │
│ │ Node 1 数据 │─ ─ ─ ─ ─ ─ ─ ─►│ Node 2 数据 │ │
│ │ (next: Weak) │ Weak │ (next: Weak) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ │
│ Step 1: 栈变量 a 离开作用域,drop(Rc<Node1>) │
│ ════════════════════════════════════════════ │
│ │
│ 1a. Node1 的 strong: 1→0 │
│ b │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 控制块 │ │ 控制块 │ │
│ │ strong: 0 ←改变 │◄─ ─ ─ ─ ─ ─ ─ ─│ strong: 1 │ │
│ │ weak: 1 │ │ weak: 1 │ │
│ ├─────────────────┤ ├─────────────────┤ │
│ │ Node 1 数据 │─ ─ ─ ─ ─ ─ ─ ─►│ Node 2 数据 │ │
│ │ (next: Weak) │ │ (next: Weak) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 1b. strong=0,触发 drop(Node1 数据),内部 Weak<Node2> 也被 drop │
│ → Node2 的 weak: 1→0 │
│ │
│ b │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 控制块 │ │ 控制块 │ │
│ │ strong: 0 │◄─ ─ ─ ─ ─ ─ ─ ─│ strong: 1 │ │
│ │ weak: 1 │ │ weak: 0 ←改变 │ │
│ ├─────────────────┤ ├─────────────────┤ │
│ │ 🗑️ 已释放 │ │ Node 2 数据 │ │
│ │ │ │ (next: Weak) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 1c. 检查 Node1:strong=0, weak=1 (>0) → 控制块保留 ✓ │
│ 检查 Node2:strong=1, weak=0 → 控制块保留 ✓(strong>0) │
│ │
│ │
│ Step 2: 栈变量 b 离开作用域,drop(Rc<Node2>) │
│ ════════════════════════════════════════════ │
│ │
│ 2a. Node2 的 strong: 1→0 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 控制块 │ │ 控制块 │ │
│ │ strong: 0 │◄─ ─ ─ ─ ─ ─ ─ ─│ strong: 0 ←改变 │ │
│ │ weak: 1 │ │ weak: 0 │ │
│ ├─────────────────┤ ├─────────────────┤ │
│ │ (已释放) │ │ Node 2 数据 │ │
│ │ │ │ (next: Weak) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 2b. strong=0,触发 drop(Node2 数据),内部 Weak<Node1> 也被 drop │
│ → Node1 的 weak: 1→0 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 控制块 │ │ 控制块 │ │
│ │ strong: 0 │ │ strong: 0 │ │
│ │ weak: 0 ←改变 │ │ weak: 0 │ │
│ ├─────────────────┤ ├─────────────────┤ │
│ │ (已释放) │ │ 🗑️ 已释放 │ │
│ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 2c. 检查 Node1:strong=0, weak=0 → 🗑️ 释放控制块! │
│ 检查 Node2:strong=0, weak=0 → 🗑️ 释放控制块! │
│ │
│ │
│ 最终状态: │
│ ══════════ │
│ │
│ 🗑️ 🗑️ │
│ (完全释放) (完全释放) │
│ │
│ │
│ ✅ 数据 + 控制块 全部正常释放,无泄漏! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
释放规则总结
┌─────────────────────────────────────────────────────────────────────────────┐
│ 释放时机 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 内存布局 │ │
│ │ ┌─────────────────┐ │ │
│ │ │ 控制块 │ ← weak_count 保护 │ │
│ │ │ strong_count │ │ │
│ │ │ weak_count │ │ │
│ │ ├─────────────────┤ │ │
│ │ │ 数据 T │ ← strong_count 保护 │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 释放数据 T: strong → 0 时 │
│ 释放控制块: strong = 0 且 weak → 0 时 │
│ │
│ ═══════════════════════════════════════════════════════════════════ │
│ │
│ 事件 │ 数据 T │ 控制块 │
│ ─────────────────┼──────────────┼──────────────────────────────── │
│ strong: 1→0 │ 🗑️ 释放 │ 检查 weak │
│ weak: 1→0 │ (已释放) │ 若 strong=0 则 🗑️ 释放 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
关键点
┌──────────────────────────────────────────────────────────────────┐
│ │
│ 为什么 Step 1 后 Node1 控制块还在? │
│ │
│ → Node2 的 Weak 还指向 Node1 │
│ → Node1 的 weak_count = 1 │
│ → 控制块必须保留,否则 Node2 的 Weak 变野指针 │
│ │
│ ──────────────────────────────────────────────────────────── │
│ │
│ 什么时候 Node1 控制块才释放? │
│ │
│ → Step 2 中 drop(Node2 数据) 时 │
│ → Node2 内部的 Weak<Node1> 被 drop │
│ → Node1 的 weak: 1→0 │
│ → 此时 strong=0, weak=0 → 释放控制块 │
│ │
└──────────────────────────────────────────────────────────────────┘
自引用 (Self-Referential Structs)
问题本质
// ❌ 这段代码无法编译
struct SelfRef {
data: String,
ptr: &str, // 想要指向 data 的内容
}
// 为什么不行?
fn main() {
let mut s = SelfRef {
data: String::from("hello"),
ptr: ???, // 此时 data 还不存在,无法获取引用
};
s.ptr = &s.data; // data 的地址
let s2 = s; // 移动! data 地址变了,ptr 成了悬垂指针!
}
┌─────────────────────────────────────────────────────────────────────────────┐
│ 移动导致自引用失效 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 移动前 (栈地址 0x1000): 移动后 (栈地址 0x2000): │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ data: String │ │ data: String │ │
│ │ ptr ─────────────────┼──┐ │ ptr ─────────────────┼──┐ │
│ │ len: 5 │ │ │ len: 5 │ │ │
│ │ cap: 5 │ │ │ cap: 5 │ │ │
│ ├────────────────────────┤ │ ├────────────────────────┤ │ │
│ │ ptr: &str ─────────────┼──┤ │ ptr: &str ─────────────┼──┼── 悬垂! │
│ │ (指向 0x1000.data) │ │ │ (仍指向 0x1000!) │ │ │
│ └────────────────────────┘ │ └────────────────────────┘ │ │
│ ▲ │ │ │
│ └────────────────┘ │ │
│ ✓ 有效 ✗ 无效! ────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ 移动 = memcpy │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 栈 (高地址 → 低地址) │
│ ═══════════════════ │
│ │
│ 移动前: 移动后: │
│ │
│ ┌─────────────────┐ 0x2000 ┌─────────────────┐ 0x2000 │
│ │ s2: ??? │ │ s2 (有效) │ ◄── 字节被复制到这里 │
│ │ │ │ [完整的数据] │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ 0x1000 ┌─────────────────┐ 0x1000 │
│ │ s (有效) │ ════► │ s (无效) │ ◄── 编译器禁止访问 │
│ │ [完整的数据] │ memcpy │ [数据还在!] │ 但字节仍在内存中 │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 堆 (不变) 堆 (不变) │
│ ═════════ ═════════ │
│ ┌─────────────────┐ 0x8000 ┌─────────────────┐ 0x8000 │
│ │ "hello" │ │ "hello" │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ 移动后的真实内存状态 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ let s1 = String::from("hello"); │
│ let s2 = s1; // 移动 │
│ │
│ 栈内存 (物理真实状态): │
│ ┌─────────────────────┐ 0x2000 │
│ │ s2: │ │
│ │ ptr: 0x8000 ──────┼───┐ ✅ 有效,编译器允许访问 │
│ │ len: 5 │ │ │
│ │ cap: 5 │ │ │
│ └─────────────────────┘ │ │
│ │ │
│ ┌─────────────────────┐ 0x1000 │
│ │ s1 (残留数据): │ │ │
│ │ ptr: 0x8000 ──────┼───┤ ❌ 无效,编译器禁止访问 │
│ │ len: 5 │ │ 但字节确实还在! │
│ │ cap: 5 │ │ │
│ └─────────────────────┘ │ │
│ │ │
│ 堆内存: │ │
│ ┌─────────────────────┐ 0x8000 │
│ │ "hello" │◄──┴── 两个指针都指向这里 │
│ └─────────────────────┘ 但只有 s2 能用 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
解决方案
使用索引代替引用
// ✅ 简单有效的方案
struct Document {
buffer: String,
tokens: Vec<(usize, usize)>, // (start, end) 索引
}
impl Document {
fn get_token(&self, idx: usize) -> &str {
let (start, end) = self.tokens[idx];
&self.buffer[start..end]
}
}
Pin + PhantomPinned
两个组件的作用
use std::pin::Pin; // 包装器,承诺不移动内部数据
use std::marker::PhantomPinned; // 标记类型,使 Pin 真正生效
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Pin<P> → "我保证不会移动 P 指向的数据" │
│ PhantomPinned → "我需要被钉住,移动我会出问题" │
│ │
│ 两者配合:Pin 才能真正阻止移动 │
│ │
└─────────────────────────────────────────────────────────────────┘
完整示例
use std::pin::Pin;
use std::marker::PhantomPinned;
struct SelfRef {
data: String,
ptr: *const String, // 裸指针指向 data
_pin: PhantomPinned, // 关键:标记为不可移动
}
impl SelfRef {
// 第一步:创建(ptr 暂时为空)
fn new(data: String) -> Self {
SelfRef {
data,
ptr: std::ptr::null(),
_pin: PhantomPinned,
}
}
// 第二步:钉住后初始化自引用
fn init(self: Pin<&mut Self>) {
unsafe {
let this = self.get_unchecked_mut();
this.ptr = &this.data; // 现在安全了,不会再移动
}
}
// 访问方法
fn get_data(self: Pin<&Self>) -> &str {
unsafe { &*self.ptr }
}
}
fn main() {
// 使用 Box::pin 钉在堆上
let mut pinned = Box::pin(SelfRef::new("hello".into()));
pinned.as_mut().init();
println!("{}", pinned.as_ref().get_data()); // "hello"
// let moved = *pinned; // ❌ 编译错误!无法移出
}
为什么需要 PhantomPinned?
// 没有 PhantomPinned 的类型默认实现 Unpin
struct Normal { data: String } // Normal: Unpin
// Pin 对 Unpin 类型没有限制
let mut n = Normal { data: "hi".into() };
let pinned = Pin::new(&mut n);
let moved = *pinned.get_mut(); // ✅ 可以移动!Pin 形同虚设
// 有 PhantomPinned 则不实现 Unpin
struct Pinned {
data: String,
_pin: PhantomPinned // Pinned: !Unpin
}
// Pin 真正生效
// pinned.get_mut() // ❌ 方法不存在,只有 get_unchecked_mut()
ouroboros
添加依赖
[dependencies]
ouroboros = "0.18.5"
基本用法
use ouroboros::self_referencing;
#[self_referencing]
struct SelfRef {
data: String,
#[borrows(data)]
data_ref: &'this str,
}
fn main() {
let s = SelfRefBuilder {
data: "hello".into(),
data_ref_builder: |data| data.as_str(),
}.build();
s.with_data_ref(|r| println!("{}", r));
}
可变访问
use ouroboros::self_referencing;
#[self_referencing]
struct MutableSelf {
data: String,
#[borrows(mut data)] // 注意:mut 在这里
slice: &'this mut str,
}
fn main() {
let mut s = MutableSelfBuilder {
data: "hello".into(),
slice_builder: |data| data.as_mut_str(),
}.build();
s.with_slice_mut(|s| {
s.make_ascii_uppercase();
});
s.with_slice(|s| println!("{}", s)); // "HELLO"
}
ouroboros 宏展开解析
这些都是 ouroboros 宏自动生成的
┌─────────────────────────────────────────────────────────────────┐
│ 'this → ouroboros 定义的特殊生命周期占位符 │
│ SelfRefBuilder → 宏根据结构体名生成的 Builder 结构体 │
│ data_ref_builder → 宏根据字段名生成的初始化闭包字段 │
└─────────────────────────────────────────────────────────────────┘
感兴趣的同学可以使用 cargo expand 来查看展开后的代码。
cargo install cargo-expand
cargo expand
命名规则
┌────────────────────────────────────────────────────────────────────┐
│ 对于 #[self_referencing] struct Foo { ... } │
├────────────────────────────────────────────────────────────────────┤
│ │
│ 类型名 作用 │
│ ───────────────────────────────────────────────────────────── │
│ Foo 主结构体 │
│ FooBuilder Builder 结构体 │
│ FooHeads 非借用字段的所有权集合 │
│ BorrowedFields with() 中使用的引用集合 │
│ BorrowedMutFields with_mut() 中使用的可变引用集合 │
│ │
├────────────────────────────────────────────────────────────────────┤
│ │
│ 方法名 作用 │
│ ───────────────────────────────────────────────────────────── │
│ Foo::new() 直接构造 │
│ Foo::try_new() 可能失败的构造 │
│ Foo::try_new_or_recover() 失败时返回 heads │
│ FooBuilder::build() 从 builder 构造 │
│ │
│ .with_xxx() 访问字段 xxx │
│ .with_xxx_mut() 可变访问字段 xxx │
│ .with() 访问所有字段 │
│ .with_mut() 可变访问所有字段 │
│ .borrow_xxx() 直接引用 xxx(仅非自引用字段) │
│ .borrow_xxx_mut() 直接可变引用 xxx │
│ .into_heads() 消耗 self,返回 heads │
│ │
└───────────────────────────────────────────────────────────────────
完整对照示例
use ouroboros::self_referencing;
#[self_referencing]
struct Parser { // → 生成 ParserBuilder
source: String,
#[borrows(source)]
tokens: Vec<&'this str>, // → 生成 tokens_builder 字段
}
fn main() {
// 使用生成的 Builder
let p = ParserBuilder {
source: "hello world".into(),
// tokens_builder: 一个闭包,接收 &source,返回 tokens
tokens_builder: |src: &String| {
src.split_whitespace().collect()
},
}.build();
// 使用生成的访问方法
p.with_tokens(|tokens| { // with_tokens 来自字段名 tokens
println!("{:?}", tokens); // ["hello", "world"]
});
p.with_source(|s| { // 普通字段也有 with_xxx
println!("{}", s);
});
}
为什么需要闭包 builder?
┌─────────────────────────────────────────────────────────────────┐
│ 问题:自引用字段依赖其他字段,必须按顺序初始化 │
│ │
│ tokens 引用 source → source 必须先存在 │
│ │
│ 解决:用闭包延迟执行 │
│ │
│ ParserBuilder { │
│ source: "...", // 1. 先有 source │
│ tokens_builder: |src| { // 2. 闭包等 source 就位后调用 │
│ src.split()... // 此时 src 地址已固定 │
│ } │
│ }.build() // 3. build 时按顺序执行 │
│ │
└─────────────────────────────────────────────────────────────────┘
总结:'this、XxxBuilder、xxx_builder、with_xxx() 全部是 ouroboros 宏生成的。
深入类型
dyn trait
基本概念
dyn Trait 是 trait 对象,用于实现运行时多态(动态分发)。
// dyn 关键字表示"动态的",后面跟 trait 名
// dyn Trait 本身是一个 DST(动态大小类型)
fn main() {
// 必须通过指针使用:&dyn Trait、Box<dyn Trait>、Rc<dyn Trait> 等
let x: &dyn std::fmt::Debug = &42;
let y: Box<dyn std::fmt::Debug> = Box::new("hello");
}
静态分发 vs 动态分发
trait Animal {
fn speak(&self);
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn speak(&self) { println!("Woof!"); }
}
impl Animal for Cat {
fn speak(&self) { println!("Meow!"); }
}
// ============ 静态分发(泛型)============
// 编译时确定具体类型,为每个类型生成专门的代码(单态化)
fn static_speak<T: Animal>(animal: &T) {
animal.speak();
}
// 编译器生成:
// fn static_speak_dog(animal: &Dog) { ... }
// fn static_speak_cat(animal: &Cat) { ... }
// ============ 动态分发(dyn Trait)============
// 运行时通过 vtable 查找方法
fn dynamic_speak(animal: &dyn Animal) {
animal.speak(); // 运行时查表
}
fn main() {
let dog = Dog;
let cat = Cat;
// 静态分发
static_speak(&dog);
static_speak(&cat);
// 动态分发
dynamic_speak(&dog);
dynamic_speak(&cat);
}
┌─────────────────────────────────────────────────────────────────────┐
│ 静态分发 vs 动态分发 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 特性 静态分发 (impl Trait/泛型) 动态分发 (dyn Trait)│
│ ───────────────────────────────────────────────────────────────── │
│ 类型确定时机 编译时 运行时 │
│ 性能 更快(可内联) 有虚表查找开销 │
│ 二进制大小 更大(每类型一份代码) 更小(共享代码) │
│ 异构集合 ❌ 不支持 ✅ 支持 │
│ 编译时间 更长 更短 │
│ 语法 impl Trait 或 <T: Trait> dyn Trait │
│ │
└─────────────────────────────────────────────────────────────────────┘
Trait 对象的内存结构
use std::mem::size_of;
fn main() {
// 普通引用:8 字节(64位系统)
println!("{}", size_of::<&i32>()); // 8
// trait 对象引用:16 字节(胖指针)
println!("{}", size_of::<&dyn Send>()); // 16
println!("{}", size_of::<Box<dyn Send>>()); // 16
}
┌─────────────────────────────────────────────────────────────────────┐
│ &dyn Trait 胖指针结构 │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ data_ptr (8 bytes) │ vtable_ptr (8 bytes) │ │
│ │ 指向实际数据 │ 指向虚函数表 │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ vtable(虚函数表)内容: │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ drop_fn │ 析构函数指针 │ │
│ │ size │ 类型大小 │ │
│ │ align │ 类型对齐 │ │
│ │ method_1 │ trait 方法 1 的函数指针 │ │
│ │ method_2 │ trait 方法 2 的函数指针 │ │
│ │ ... │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
为什么需要 dyn Trait
异构集合
trait Drawable {
fn draw(&self);
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Drawable for Circle {
fn draw(&self) { println!("Drawing circle r={}", self.radius); }
}
impl Drawable for Rectangle {
fn draw(&self) { println!("Drawing rect {}x{}", self.width, self.height); }
}
fn main() {
// ❌ 泛型方式:Vec 只能存一种类型
// let shapes: Vec<impl Drawable> = vec![Circle{}, Rectangle{}]; // 错误!
// ✅ trait 对象:可以存不同类型
let shapes: Vec<Box<dyn Drawable>> = vec![
Box::new(Circle { radius: 1.0 }),
Box::new(Rectangle { width: 2.0, height: 3.0 }),
];
for shape in &shapes {
shape.draw(); // 运行时多态
}
}
返回不同类型
trait Animal {
fn speak(&self) -> &str;
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn speak(&self) -> &str { "Woof" }
}
impl Animal for Cat {
fn speak(&self) -> &str { "Meow" }
}
// 根据条件返回不同类型
fn get_animal(is_dog: bool) -> Box<dyn Animal> {
if is_dog {
Box::new(Dog)
} else {
Box::new(Cat)
}
}
fn main() {
let animal = get_animal(true);
println!("{}", animal.speak());
}
存储回调/闭包
// 闭包每个都是独特的类型,需要 dyn 来统一存储
struct Button {
on_click: Box<dyn Fn()>,
}
impl Button {
fn new(callback: impl Fn() + 'static) -> Self {
Button {
on_click: Box::new(callback),
}
}
fn click(&self) {
(self.on_click)();
}
}
fn main() {
let btn1 = Button::new(|| println!("Button 1 clicked"));
let btn2 = Button::new(|| println!("Button 2 clicked"));
btn1.click();
btn2.click();
}
对象安全(Object Safety)
不是所有 trait 都能变成 dyn Trait,必须是"对象安全"的。
对象安全的要求
// ✅ 对象安全的 trait
trait Safe {
fn method(&self);
fn method_with_param(&self, x: i32) -> i32;
}
// ❌ 不安全:有泛型方法
trait NotSafe1 {
fn generic_method<T>(&self, t: T); // 无法确定 vtable 大小
}
// ❌ 不安全:返回 Self
trait NotSafe2 {
fn clone(&self) -> Self; // 不知道 Self 的具体大小
}
// ❌ 不安全:方法需要 Self: Sized
trait NotSafe3 {
fn by_value(self); // 需要知道 Self 大小
}
// ❌ 不安全:关联函数(没有 self 参数)
trait NotSafe4 {
fn associated() -> i32; // 不知道是哪个类型的方法
}
┌─────────────────────────────────────────────────────────────────────┐
│ 对象安全规则(所有方法必须满足) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 不能有泛型类型参数 │
│ 2. 第一个参数必须是 self 相关类型: │
│ - &self │
│ - &mut self │
│ - self: Box<Self> │
│ - self: Rc<Self> │
│ - self: Arc<Self> │
│ - self: Pin<&Self> │
│ 3. 返回值不能是 Self │
│ 4. 没有 where Self: Sized 约束 │
│ │
└─────────────────────────────────────────────────────────────────────┘
绕过限制的技巧
trait MyTrait {
// 泛型方法加 where Self: Sized,从 dyn 中排除
fn generic<T>(&self, t: T) where Self: Sized;
// 其他方法仍可用于 dyn
fn normal(&self);
}
struct Foo;
impl MyTrait for Foo {
fn generic<T>(&self, t: T) { }
fn normal(&self) { println!("normal"); }
}
fn main() {
let obj: &dyn MyTrait = &Foo;
obj.normal(); // ✅ 可以调用
// obj.generic(1); // ❌ 不能通过 dyn 调用
}
常见的 dyn Trait 用法
错误处理
use std::error::Error;
// Box<dyn Error> 可以包装任何错误类型
fn might_fail() -> Result<(), Box<dyn Error>> {
let _file = std::fs::File::open("nonexistent.txt")?;
let _num: i32 = "not a number".parse()?;
Ok(())
}
fn main() {
if let Err(e) = might_fail() {
println!("Error: {}", e);
}
}
闭包存储
type Callback = Box<dyn Fn(i32) -> i32>;
type AsyncCallback = Box<dyn Fn() + Send + Sync>; // 可跨线程
struct EventHandler {
callbacks: Vec<Box<dyn Fn(&str)>>,
}
impl EventHandler {
fn new() -> Self {
EventHandler { callbacks: vec![] }
}
fn add_callback(&mut self, cb: impl Fn(&str) + 'static) {
self.callbacks.push(Box::new(cb));
}
fn trigger(&self, event: &str) {
for cb in &self.callbacks {
cb(event);
}
}
}
插件系统
trait Plugin {
fn name(&self) -> &str;
fn execute(&self);
}
struct PluginManager {
plugins: Vec<Box<dyn Plugin>>,
}
impl PluginManager {
fn new() -> Self {
PluginManager { plugins: vec![] }
}
fn register(&mut self, plugin: impl Plugin + 'static) {
self.plugins.push(Box::new(plugin));
}
fn run_all(&self) {
for plugin in &self.plugins {
println!("Running: {}", plugin.name());
plugin.execute();
}
}
}
多个 trait 约束
use std::fmt::Debug;
// 使用 + 组合多个 trait
fn print_debug(val: &(dyn Debug + Send + Sync)) {
println!("{:?}", val);
}
// Box 版本
fn boxed_multi() -> Box<dyn Debug + Send + Sync> {
Box::new(42)
}
Sized 与动态大小类型 (DST)
Sized Trait 基础
// Sized 是一个自动实现的 marker trait
// 表示类型在编译时有已知固定大小
fn foo<T>(t: T) {} // 等价于 fn foo<T: Sized>(t: T) {}
fn bar<T: ?Sized>(t: &T) {} // ?Sized 表示"可能不是 Sized"
类型大小分类
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 类型大小分类 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Sized 类型(编译时已知大小) │
│ ├── 原始类型: i32, f64, bool, char │
│ ├── 固定数组: [T; N] │
│ ├── 元组: (A, B, C) │
│ ├── 结构体/枚举(所有字段都是 Sized) │
│ └── 指针/引用: &T, &mut T, *const T, Box<T> │
│ │
│ DST - 动态大小类型(编译时大小未知) │
│ ├── str 字符串切片 │
│ ├── [T] 数组切片 │
│ └── dyn Trait trait 对象 │
│ │
│ ZST - 零大小类型 │
│ ├── () 单元类型 │
│ ├── struct Foo; 空结构体 │
│ └── PhantomData<T> │
│ │
└─────────────────────────────────────────────────────────────────────┘
DST 与胖指针
use std::mem::size_of;
fn main() {
// ===== 普通指针(瘦指针)=====
println!("{}", size_of::<&i32>()); // 8 (64位系统)
println!("{}", size_of::<&[i32; 10]>()); // 8
println!("{}", size_of::<Box<i32>>()); // 8
// ===== 胖指针(包含额外元数据)=====
println!("{}", size_of::<&str>()); // 16 = 指针 + 长度
println!("{}", size_of::<&[i32]>()); // 16 = 指针 + 长度
println!("{}", size_of::<&dyn Send>()); // 16 = 指针 + vtable指针
println!("{}", size_of::<Box<dyn Send>>()); // 16
}
┌─────────────────────────────────────────────────────────────────────┐
│ 胖指针内部结构 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ &[T] / &str: │
│ ┌──────────────────┬──────────────────┐ │
│ │ data pointer │ length │ │
│ │ (8 bytes) │ (8 bytes) │ │
│ └──────────────────┴──────────────────┘ │
│ │
│ &dyn Trait: │
│ ┌──────────────────┬──────────────────┐ │
│ │ data pointer │ vtable pointer │ │
│ │ (8 bytes) │ (8 bytes) │ │
│ └──────────────────┴──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
?Sized 约束的使用
// 默认 T: Sized,不能接受 DST
fn print_sized<T: std::fmt::Debug>(val: &T) {
println!("{:?}", val);
}
// 使用 ?Sized 允许 DST
fn print_any<T: std::fmt::Debug + ?Sized>(val: &T) {
println!("{:?}", val);
}
fn main() {
let s: &str = "hello";
let arr: &[i32] = &[1, 2, 3];
// print_sized(s); // ❌ str 不是 Sized
print_any(s); // ✅
print_any(arr); // ✅
print_any(&42i32); // ✅ Sized 类型也可以
}
结构体中的 DST
// DST 只能作为结构体的最后一个字段
struct MySlice<T> {
len: usize,
data: [T], // DST 必须在最后
}
// 常见用法:自定义切片类型
#[repr(transparent)]
struct Path {
inner: std::ffi::OsStr, // DST
}
// 创建方式:通常通过指针转换
impl Path {
fn new<S: AsRef<std::ffi::OsStr> + ?Sized>(s: &S) -> &Path {
unsafe { &*(s.as_ref() as *const std::ffi::OsStr as *const Path) }
}
}
零大小类型 (ZST)
ZST 类型示例
use std::mem::size_of;
use std::marker::PhantomData;
// 各种 ZST
struct Empty;
struct NoFields {}
struct Marker;
fn main() {
println!("{}", size_of::<()>()); // 0
println!("{}", size_of::<Empty>()); // 0
println!("{}", size_of::<NoFields>()); // 0
println!("{}", size_of::<PhantomData<i32>>()); // 0
println!("{}", size_of::<[i32; 0]>()); // 0
// ZST 不占内存,但有唯一地址
let a = Empty;
let b = Empty;
println!("{:p}", &a); // 有地址
println!("{:p}", &b); // 不同地址
}
PhantomData 用途
use std::marker::PhantomData;
// ===== 用途1:标记未使用的类型参数 =====
struct Slice<'a, T> {
ptr: *const T,
len: usize,
_marker: PhantomData<&'a T>, // 告诉编译器:我们"拥有" &'a T
}
// ===== 用途2:添加生命周期约束 =====
struct Iter<'a, T> {
ptr: *const T,
end: *const T,
_marker: PhantomData<&'a T>,
}
// ===== 用途3:类型状态模式 =====
struct Builder<State> {
data: String,
_state: PhantomData<State>,
}
struct Initial;
struct Configured;
struct Built;
impl Builder<Initial> {
fn new() -> Self {
Builder { data: String::new(), _state: PhantomData }
}
fn configure(self, data: &str) -> Builder<Configured> {
Builder { data: data.into(), _state: PhantomData }
}
}
impl Builder<Configured> {
fn build(self) -> Builder<Built> {
Builder { data: self.data, _state: PhantomData }
}
}
// ===== 用途4:Drop 检查 =====
struct DropChecker<T> {
ptr: *mut T,
_marker: PhantomData<T>, // 表示我们逻辑上拥有 T
}
impl<T> Drop for DropChecker<T> {
fn drop(&mut self) {
// 析构时可能访问 T 的内容
unsafe { std::ptr::drop_in_place(self.ptr); }
}
}
PhantomData 变体
use std::marker::PhantomData;
struct Container<T> {
ptr: *const T,
// 不同的所有权语义
_owned: PhantomData<T>, // 拥有 T(会影响 drop check)
_ref: PhantomData<&'static T>, // 只是引用 T
_fn: PhantomData<fn(T)>, // 协变
_fn_ret: PhantomData<fn() -> T>, // 逆变
}
Newtype 模式
基本定义
// Newtype:用元组结构体包装现有类型
struct Meters(f64);
struct Seconds(f64);
struct UserId(u64);
struct Email(String);
核心用途
// ===== 用途1:类型安全,防止混淆 =====
struct Meters(f64);
struct Feet(f64);
fn calculate_distance(m: Meters) -> Meters {
Meters(m.0 * 2.0)
}
fn main() {
let m = Meters(100.0);
let f = Feet(100.0);
calculate_distance(m); // ✅
// calculate_distance(f); // ❌ 类型不匹配
}
// ===== 用途2:为外部类型实现外部 trait(绕过孤儿规则)=====
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
// ===== 用途3:隐藏内部实现 =====
pub struct PublicApi(InternalType); // InternalType 是私有的
// ===== 用途4:添加语义约束 =====
struct NonEmpty<T>(Vec<T>);
impl<T> NonEmpty<T> {
fn new(first: T) -> Self {
NonEmpty(vec![first])
}
fn first(&self) -> &T {
&self.0[0] // 永远安全,因为构造时保证非空
}
}
// ===== 用途5:细化类型约束 =====
struct SortedVec<T: Ord>(Vec<T>);
impl<T: Ord> SortedVec<T> {
fn new(mut v: Vec<T>) -> Self {
v.sort();
SortedVec(v)
}
fn binary_search(&self, x: &T) -> Option<usize> {
self.0.binary_search(x).ok()
}
}
Newtype 便捷实现
use std::ops::{Deref, DerefMut, Add};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
struct Meters(f64);
// 实现 Deref 可透明访问内部值
impl Deref for Meters {
type Target = f64;
fn deref(&self) -> &f64 {
&self.0
}
}
// 实现运算符
impl Add for Meters {
type Output = Self;
fn add(self, other: Self) -> Self {
Meters(self.0 + other.0)
}
}
// From/Into 转换
impl From<f64> for Meters {
fn from(v: f64) -> Self {
Meters(v)
}
}
impl From<Meters> for f64 {
fn from(m: Meters) -> Self {
m.0
}
}
fn main() {
let m = Meters(10.0);
// Deref 让我们可以直接调用 f64 的方法
println!("{}", m.abs());
println!("{}", m.sin());
// 运算
let total = Meters(10.0) + Meters(20.0);
// 转换
let m: Meters = 5.0.into();
let v: f64 = m.into();
}
使用 derive_more 简化
# Cargo.toml
[dependencies]
derive_more = "0.99"
use derive_more::{From, Into, Deref, DerefMut, Add, Display};
#[derive(Debug, Clone, Copy, From, Into, Deref, Add, Display)]
#[display(fmt = "{}m", _0)]
struct Meters(f64);
fn main() {
let m: Meters = 10.0.into();
let sum = m + Meters(5.0);
println!("{}", sum); // "15m"
}
类型别名
基本语法
// type 关键字定义类型别名
type Kilometers = i32;
type Thunk = Box<dyn Fn() + Send + 'static>;
type Result<T> = std::result::Result<T, std::io::Error>;
fn main() {
let x: Kilometers = 5;
let y: i32 = 10;
// 类型别名与原类型完全等价,可以互操作
let z = x + y; // ✅ 都是 i32
}
类型别名常见用途
// ===== 简化复杂类型 =====
type Callback = Box<dyn Fn(i32) -> i32 + Send + Sync + 'static>;
type ParseResult<'a> = Result<(&'a str, Token), ParseError>;
// ===== 模块级 Result 别名(std 库常用模式)=====
pub type Result<T> = std::result::Result<T, MyError>;
pub fn foo() -> Result<i32> { // 等价于 Result<i32, MyError>
Ok(42)
}
// ===== 泛型别名 =====
type Pair<T> = (T, T);
type StringMap<V> = std::collections::HashMap<String, V>;
fn main() {
let p: Pair<i32> = (1, 2);
let mut m: StringMap<i32> = StringMap::new();
m.insert("a".into(), 1);
}
// ===== trait 关联类型别名 =====
trait Container {
type Item;
type Iter<'a>: Iterator<Item = &'a Self::Item> where Self: 'a;
}
// ===== never 类型别名 =====
type Never = !; // 仅在 nightly
类型转换
as 强制转换
fn main() {
// ===== 数值类型转换 =====
let a: i32 = 42;
let b: i64 = a as i64; // 扩展
let c: i16 = a as i16; // 截断(可能丢失数据)
let d: f64 = a as f64; // 整数转浮点
let e: i32 = 3.14 as i32; // 浮点转整数(截断小数)
// ===== 指针转换 =====
let ptr: *const i32 = &42;
let addr: usize = ptr as usize;
let ptr2: *const i32 = addr as *const i32;
// ===== 引用转裸指针 =====
let r: &i32 = &42;
let p: *const i32 = r as *const i32;
// ===== 枚举转整数 =====
#[repr(u8)]
enum Color { Red = 1, Green = 2, Blue = 3 }
let n: u8 = Color::Red as u8; // 1
// ===== 字符转换 =====
let c: char = 'A';
let n: u32 = c as u32; // 65
let c2: char = 66u8 as char; // 'B'
}
From/Into trait
- 消耗原值:调用 .into() 会获取所有权并转换为目标类型。
- 自动实现:实现了 From\<T> 的类型会自动拥有 Into\<T>。
- 泛型约束常用:在泛型函数中使用 T: Into\<U> 可以让函数接受更多可转换的类型。
use std::convert::From;
// ===== 实现 From,自动获得 Into =====
struct Meters(f64);
impl From<f64> for Meters {
fn from(v: f64) -> Self {
Meters(v)
}
}
impl From<i32> for Meters {
fn from(v: i32) -> Self {
Meters(v as f64)
}
}
fn main() {
// From
let m1 = Meters::from(10.5);
let m2 = Meters::from(10);
// Into(自动实现)
let m3: Meters = 10.5.into();
let m4: Meters = 10.into();
// 函数参数中使用 Into
fn process(m: impl Into<Meters>) {
let meters = m.into();
println!("{}", meters.0);
}
process(10.5); // f64
process(10i32); // i32
}
TryFrom/TryInto(可能失败的转换)
use std::convert::TryFrom;
use std::num::TryFromIntError;
// ===== 标准库中的 TryFrom =====
fn main() {
// 可能失败的整数转换
let a: i32 = 1000;
let b: Result<u8, _> = u8::try_from(a); // Err,溢出
let c: i32 = 100;
let d: u8 = u8::try_from(c).unwrap(); // Ok(100)
// TryInto
let e: Result<u8, _> = a.try_into();
}
// ===== 自定义 TryFrom =====
#[derive(Debug)]
struct PositiveInt(i32);
#[derive(Debug)]
struct NegativeError;
impl TryFrom<i32> for PositiveInt {
type Error = NegativeError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value > 0 {
Ok(PositiveInt(value))
} else {
Err(NegativeError)
}
}
}
fn example() {
let p: Result<PositiveInt, _> = 42.try_into(); // Ok
let n: Result<PositiveInt, _> = (-1).try_into(); // Err
}
AsRef/AsMut(廉价引用转换)
// AsRef:借用转换,零成本
fn print_path<P: AsRef<std::path::Path>>(path: P) {
println!("{}", path.as_ref().display());
}
fn print_bytes<B: AsRef<[u8]>>(bytes: B) {
println!("{:?}", bytes.as_ref());
}
fn main() {
// 多种类型都实现了 AsRef<Path>
print_path("hello.txt"); // &str
print_path(String::from("hello.txt")); // String
print_path(std::path::Path::new("hello.txt")); // Path
// 多种类型都实现了 AsRef<[u8]>
print_bytes("hello"); // &str
print_bytes(vec![1, 2, 3]); // Vec<u8>
print_bytes([1, 2, 3]); // [u8; 3]
}
// 自定义实现
struct MyString(String);
impl AsRef<str> for MyString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsRef<[u8]> for MyString {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
Borrow/BorrowMut
use std::borrow::Borrow;
use std::collections::HashMap;
// Borrow 主要用于集合类型的键查找
fn main() {
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("hello".into(), 42);
// 可以用 &str 查找 String 键
let v = map.get("hello"); // 不需要创建 String
// 这得益于 String: Borrow<str>
}
// Borrow vs AsRef
// - Borrow 有额外约束:Hash/Eq/Ord 必须一致
// - AsRef 是纯粹的引用转换
Deref 强制转换
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn hello(name: &str) {
println!("Hello, {}", name);
}
fn main() {
let s = MyBox(String::from("world"));
// Deref 强制转换链:
// &MyBox<String> -> &String -> &str
hello(&s); // ✅ 自动解引用
// 等价于
hello(&(*s)[..]);
}
类型转换总结
┌─────────────────────────────────────────────────────────────────────┐
│ 类型转换方式对比 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 方式 成本 安全性 用途 │
│ ────────────────────────────────────────────────────────────── │
│ as 零/低 不安全 原始类型转换、指针转换 │
│ From/Into 可变 安全 值类型转换(不会失败) │
│ TryFrom/Into 可变 安全 可能失败的转换 │
│ AsRef/AsMut 零 安全 引用借用 │
│ Borrow 零 安全 集合键查找 │
│ Deref 零 安全 智能指针透明访问 │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 转换关系链: │
│ │
│ From<T> ──自动实现──> Into<T> │
│ TryFrom<T> ──自动实现──> TryInto<T> │
│ Deref ──启用──> 自动解引用强制转换 │
│ │
└─────────────────────────────────────────────────────────────────────┘
进阶模式
类型状态模式(Typestate Pattern)
use std::marker::PhantomData;
// 状态类型(零大小)
struct Locked;
struct Unlocked;
struct Door<State> {
_state: PhantomData<State>,
}
impl Door<Locked> {
fn unlock(self) -> Door<Unlocked> {
println!("Unlocking...");
Door { _state: PhantomData }
}
}
impl Door<Unlocked> {
fn lock(self) -> Door<Locked> {
println!("Locking...");
Door { _state: PhantomData }
}
fn open(&self) {
println!("Opening door");
}
}
fn new_door() -> Door<Locked> {
Door { _state: PhantomData }
}
fn main() {
let door = new_door(); // Locked
// door.open(); // ❌ 编译错误!
let door = door.unlock(); // Unlocked
door.open(); // ✅
let door = door.lock(); // Locked
// door.open(); // ❌ 又不能开了
}
透明 Newtype
#[repr(transparent)] // 保证与内部类型有相同的内存布局
struct Wrapper(u32);
// 这允许安全的指针转换
impl Wrapper {
fn from_ref(r: &u32) -> &Wrapper {
unsafe { &*(r as *const u32 as *const Wrapper) }
}
fn from_slice(s: &[u32]) -> &[Wrapper] {
unsafe { std::mem::transmute(s) }
}
}
不可构造类型
// 使用枚举创建不可构造的类型
enum Void {}
// 或使用 never 类型(nightly)
// type Never = !;
// 用途:标记不可能发生的情况
fn diverge() -> Void {
loop {} // 永不返回
}
// Result<T, Void> 表示不可能失败
fn always_works() -> Result<i32, Void> {
Ok(42)
}
fn main() {
// 可以安全地 unwrap,因为 Err 情况不可能发生
let v = always_works().unwrap_or_else(|void| match void {});
}
全局变量
核心概念对比
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 全局变量方案 │
├──────────────┬──────────────┬───────────────┬──────────────────────┤
│ 方案 │ 可变性 │ 初始化时机 │ 线程安全 │
├──────────────┼──────────────┼───────────────┼──────────────────────┤
│ const │ 不可变 │ 编译期 │ ✓ │
│ static │ 不可变 │ 编译期 │ ✓ │
│ static mut │ 可变 │ 编译期 │ ✗ (unsafe) │
│ lazy_static! │ 不可变* │ 首次访问 │ ✓ │
│ OnceLock │ 不可变* │ 首次访问 │ ✓ (std 1.70+) │
│ Mutex/RwLock │ 可变 │ 首次访问 │ ✓ │
└──────────────┴──────────────┴───────────────┴──────────────────────┘
* 内部可变性除外
基础:const vs static
// ============ const:编译期常量(内联替换)============
const MAX_SIZE: usize = 1024;
const PI: f64 = 3.14159265359;
const GREETING: &str = "Hello, Rust!";
// ============ static:固定内存地址 ============
static VERSION: &str = "1.0.0";
static COUNTER_INIT: i32 = 0;
fn main() {
// const 会被内联,每次使用可能复制值
println!("Max: {}", MAX_SIZE);
// static 有固定地址,可以取引用
let ver_ptr: &'static str = VERSION;
println!("Version at {:p}", ver_ptr);
}
选择原则:
- 简单标量/字符串 → 优先用 const
- 需要固定地址或大型数据 → 用 static
可变全局变量(危险区域)
static mut(不推荐)
static mut COUNTER: i32 = 0;
fn main() {
unsafe {
COUNTER += 1; // 必须在 unsafe 块中
println!("Counter: {}", COUNTER);
}
}
// ⚠️ 问题:多线程下是数据竞争,完全由程序员保证安全
安全的运行时初始化方案
std::sync::OnceLock
Rust 1.70+ 推荐:
use std::sync::OnceLock;
// 复杂类型的全局变量
static CONFIG: OnceLock<Config> = OnceLock::new();
struct Config {
db_url: String,
max_conn: u32,
}
fn get_config() -> &'static Config {
CONFIG.get_or_init(|| {
// 只执行一次,线程安全
Config {
db_url: std::env::var("DB_URL").unwrap_or_default(),
max_conn: 100,
}
})
}
fn main() {
println!("DB: {}", get_config().db_url);
println!("Max: {}", get_config().max_conn);
}
lazy_static!
// Cargo.toml: lazy_static = "1.4"
use lazy_static::lazy_static;
use std::collections::HashMap;
lazy_static! {
static ref CACHE: HashMap<&'static str, i32> = {
let mut m = HashMap::new();
m.insert("one", 1);
m.insert("two", 2);
m
};
static ref REGEX: regex::Regex =
regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
}
fn main() {
println!("one = {}", CACHE.get("one").unwrap());
println!("Valid: {}", REGEX.is_match("2024-01-15"));
}
once_cell
// Cargo.toml: once_cell = "1.19"
use once_cell::sync::Lazy;
static LOGGER: Lazy<Logger> = Lazy::new(|| {
Logger::new("app.log")
});
// 局部懒加载也可以
fn expensive_computation() -> &'static Vec<i32> {
static RESULT: Lazy<Vec<i32>> = Lazy::new(|| {
(0..1000000).collect()
});
&RESULT
}
线程安全的可变全局变量
Mutex 方案
use std::sync::Mutex;
use std::sync::OnceLock;
static GLOBAL_STATE: OnceLock<Mutex<AppState>> = OnceLock::new();
struct AppState {
count: u64,
name: String,
}
fn state() -> &'static Mutex<AppState> {
GLOBAL_STATE.get_or_init(|| {
Mutex::new(AppState {
count: 0,
name: String::from("App"),
})
})
}
fn main() {
// 安全地修改全局状态
{
let mut s = state().lock().unwrap();
s.count += 1;
s.name = String::from("Updated");
}
println!("Count: {}", state().lock().unwrap().count);
}
RwLock
读多写少场景。
use std::sync::RwLock;
use once_cell::sync::Lazy;
static SETTINGS: Lazy<RwLock<Settings>> = Lazy::new(|| {
RwLock::new(Settings::default())
});
#[derive(Default)]
struct Settings {
debug: bool,
level: u8,
}
fn main() {
// 多线程可同时读
let debug = SETTINGS.read().unwrap().debug;
// 独占写
SETTINGS.write().unwrap().level = 5;
}
原子类型
use std::sync::atomic::{AtomicU64, Ordering};
static REQUEST_COUNT: AtomicU64 = AtomicU64::new(0);
fn handle_request() {
REQUEST_COUNT.fetch_add(1, Ordering::Relaxed);
}
fn main() {
handle_request();
handle_request();
println!("Total requests: {}", REQUEST_COUNT.load(Ordering::Relaxed));
}
方案选择流程图
需要全局变量?
│
├── 编译期已知值?
│ ├── 是 → const(简单值)或 static(需要地址)
│ └── 否 ↓
│
├── 运行时初始化,只读?
│ └── OnceLock / Lazy / lazy_static!
│
├── 需要可变?
│ ├── 简单整数 → AtomicXxx
│ ├── 读多写少 → RwLock<T>
│ └── 一般情况 → Mutex<T>
│
└── 避免全局变量?
└── 依赖注入 / 传参(最佳实践)
⚠️ 黄金法则:尽量避免全局可变状态,优先使用依赖注入传递配置和状态。全局变量会增加代码耦合度和测试难度。
多线程
核心概念总览
┌─────────────────────────────────────────────────────────────────────────┐
│ Rust 并发安全保证 │
├─────────────────────────────────────────────────────────────────────────┤
│ 编译期检查: Send + Sync trait → 消除数据竞争 │
│ 运行时保护: Mutex / RwLock / Atomic → 安全共享可变状态 │
│ 所有权转移: move 闭包 → 明确数据归属 │
└─────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 消息传递 │ │ 共享状态 │ │ 原子操作 │
│ (Channel) │ │ (Mutex/Arc) │ │ (Atomic*) │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ 无共享,安全 │ │ 锁保护,灵活 │ │ 无锁,高性能 │
│ 适合流水线 │ │ 适合复杂状态 │ │ 适合简单计数 │
└──────────────┘ └──────────────┘ └──────────────┘
线程基础
创建与等待
use std::thread;
use std::time::Duration;
fn main() {
// 创建线程
let handle = thread::spawn(|| {
for i in 1..5 {
println!("子线程: {}", i);
thread::sleep(Duration::from_millis(100));
}
42 // 返回值
});
// 主线程工作
for i in 1..3 {
println!("主线程: {}", i);
thread::sleep(Duration::from_millis(150));
}
// 等待并获取返回值
let result = handle.join().unwrap();
println!("子线程返回: {}", result);
}
move 闭包 —— 转移所有权
use std::thread;
fn main() {
let data = vec![1, 2, 3];
// ❌ 错误:闭包可能比 data 活得久
// let handle = thread::spawn(|| {
// println!("{:?}", data);
// });
// ✅ 正确:move 转移所有权
let handle = thread::spawn(move || {
println!("{:?}", data); // data 现在属于子线程
});
// println!("{:?}", data); // ❌ data 已被移动
handle.join().unwrap();
}
线程配置
use std::thread;
fn main() {
let builder = thread::Builder::new()
.name("worker-1".into())
.stack_size(4 * 1024 * 1024); // 4MB 栈
let handle = builder.spawn(|| {
println!("线程名: {:?}", thread::current().name());
println!("线程ID: {:?}", thread::current().id());
}).unwrap();
handle.join().unwrap();
// 获取 CPU 核心数
println!("CPU 核心数: {}", thread::available_parallelism().unwrap());
}
消息传递(Channel)
多生产者单消费者(mpsc)
use std::sync::mpsc;
use std::thread;
fn main() {
// 创建通道
let (tx, rx) = mpsc::channel();
// 生产者线程
thread::spawn(move || {
let messages = vec!["hello", "from", "thread"];
for msg in messages {
tx.send(msg).unwrap();
thread::sleep(std::time::Duration::from_millis(100));
}
// tx 被 drop,通道关闭
});
// 消费者(主线程)
for received in rx { // 迭代器方式接收
println!("收到: {}", received);
}
}
多生产者
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
// 克隆发送端给多个生产者
for id in 0..3 {
let tx_clone = tx.clone();
thread::spawn(move || {
tx_clone.send(format!("来自线程 {}", id)).unwrap();
});
}
drop(tx); // 重要:丢弃原始发送端
for msg in rx {
println!("{}", msg);
}
}
同步通道(有界)
use std::sync::mpsc;
use std::thread;
fn main() {
// 容量为 2 的有界通道
let (tx, rx) = mpsc::sync_channel(2);
thread::spawn(move || {
for i in 0..5 {
println!("发送 {}", i);
tx.send(i).unwrap(); // 满了会阻塞
println!("已发送 {}", i);
}
});
thread::sleep(std::time::Duration::from_secs(1));
for val in rx {
println!("收到: {}", val);
}
}
Channel 方法对比
┌─────────────┬─────────────────────────────────────────────┐
│ 方法 │ 行为 │
├─────────────┼─────────────────────────────────────────────┤
│ send() │ 发送,通道关闭返回 Err │
│ recv() │ 阻塞接收,通道关闭返回 Err │
│ try_recv() │ 非阻塞,无数据返回 Err(TryRecvError::Empty) │
│ recv_timeout() │ 超时接收 │
│ iter() │ 阻塞迭代器,通道关闭结束 │
│ try_iter() │ 非阻塞迭代器 │
└─────────────┴─────────────────────────────────────────────┘
共享状态
Arc + Mutex
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Arc: 原子引用计数,跨线程共享
// Mutex: 互斥锁,保护内部数据
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter); // 克隆 Arc(引用计数+1)
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap(); // 获取锁
*num += 1;
// 锁在 num 离开作用域时自动释放
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("结果: {}", *counter.lock().unwrap()); // 输出: 10
}
RwLock
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3]));
let mut handles = vec![];
// 多个读者
for i in 0..3 {
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
let read_guard = data.read().unwrap(); // 共享读锁
println!("读者 {}: {:?}", i, *read_guard);
}));
}
// 一个写者
{
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
let mut write_guard = data.write().unwrap(); // 独占写锁
write_guard.push(4);
println!("写者: 已添加 4");
}));
}
for h in handles {
h.join().unwrap();
}
}
锁的注意事项
use std::sync::{Arc, Mutex};
fn main() {
let data = Arc::new(Mutex::new(5));
// ❌ 死锁示例:同一线程重复获取非可重入锁
// let _a = data.lock().unwrap();
// let _b = data.lock().unwrap(); // 死锁!
// ✅ 正确:尽快释放锁
{
let mut guard = data.lock().unwrap();
*guard += 1;
} // 锁在这里释放
// ✅ 处理中毒的锁(某线程 panic 时锁会中毒)
let result = data.lock();
match result {
Ok(guard) => println!("值: {}", *guard),
Err(poisoned) => {
// 选择恢复或传播
let guard = poisoned.into_inner();
println!("恢复中毒锁,值: {}", *guard);
}
}
}
原子操作
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
use std::thread;
use std::sync::Arc;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let running = Arc::new(AtomicBool::new(true));
let mut handles = vec![];
// 工作线程
for _ in 0..4 {
let counter = Arc::clone(&counter);
let running = Arc::clone(&running);
handles.push(thread::spawn(move || {
while running.load(Ordering::Relaxed) {
counter.fetch_add(1, Ordering::SeqCst);
if counter.load(Ordering::SeqCst) >= 100 {
running.store(false, Ordering::Relaxed);
}
}
}));
}
for h in handles {
h.join().unwrap();
}
println!("最终计数: {}", counter.load(Ordering::SeqCst));
}
Ordering 说明
┌──────────────┬────────────────────────────────────────────┐
│ Ordering │ 语义 │
├──────────────┼────────────────────────────────────────────┤
│ Relaxed │ 最弱,仅保证原子性,不保证顺序 │
│ Acquire │ 读操作,阻止后续操作重排到此之前 │
│ Release │ 写操作,阻止之前操作重排到此之后 │
│ AcqRel │ 同时 Acquire + Release │
│ SeqCst │ 最强,全局顺序一致(默认安全选择) │
└──────────────┴────────────────────────────────────────────┘
同步原语
Barrier(屏障)
use std::sync::{Arc, Barrier};
use std::thread;
fn main() {
let barrier = Arc::new(Barrier::new(3)); // 等待 3 个线程
let mut handles = vec![];
for i in 0..3 {
let barrier = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
println!("线程 {} 阶段1完成", i);
barrier.wait(); // 所有线程都到达后才继续
println!("线程 {} 阶段2开始", i);
}));
}
for h in handles {
h.join().unwrap();
}
}
Condvar(条件变量)
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
// 等待线程
let pair_clone = Arc::clone(&pair);
let waiter = thread::spawn(move || {
let (lock, cvar) = &*pair_clone;
let mut ready = lock.lock().unwrap();
while !*ready {
ready = cvar.wait(ready).unwrap(); // 释放锁并等待
}
println!("收到通知,继续执行!");
});
// 通知线程
thread::sleep(std::time::Duration::from_secs(1));
let (lock, cvar) = &*pair;
{
let mut ready = lock.lock().unwrap();
*ready = true;
}
cvar.notify_one(); // 或 notify_all()
waiter.join().unwrap();
}
线程池(Rayon)
// Cargo.toml: rayon = "1.8"
use rayon::prelude::*;
fn main() {
// 并行迭代器
let sum: i64 = (1..1_000_000i64)
.into_par_iter()
.filter(|&x| x % 2 == 0)
.map(|x| x * x)
.sum();
println!("平方和: {}", sum);
// 并行排序
let mut data: Vec<i32> = (0..100000).rev().collect();
data.par_sort();
// 并行执行两个任务
let (a, b) = rayon::join(
|| expensive_computation_a(),
|| expensive_computation_b(),
);
}
fn expensive_computation_a() -> i32 { 1 }
fn expensive_computation_b() -> i32 { 2 }
Send 和 Sync
/*
┌─────────────────────────────────────────────────────────────────┐
│ Send: 类型可以安全地转移到另一个线程 │
│ Sync: 类型可以安全地被多个线程同时引用(&T 是 Send) │
├─────────────────────────────────────────────────────────────────┤
│ T: Send + Sync → 可以跨线程移动和共享 │
│ T: Send + !Sync → 可以跨线程移动,不能共享引用(如 Cell) │
│ T: !Send + Sync → 罕见 │
│ T: !Send + !Sync → 只能单线程使用(如 Rc, *mut T) │
└─────────────────────────────────────────────────────────────────┘
*/
use std::rc::Rc;
use std::sync::Arc;
use std::cell::RefCell;
use std::thread;
fn main() {
// ❌ Rc 不是 Send,不能跨线程
// let rc = Rc::new(5);
// thread::spawn(move || println!("{}", rc));
// ✅ Arc 是 Send + Sync
let arc = Arc::new(5);
thread::spawn(move || println!("{}", arc)).join().unwrap();
// ❌ RefCell 不是 Sync,不能多线程共享引用
// 如需要,使用 Mutex<T> 或 RwLock<T>
}
常见模式
生产者-消费者
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
// 多个生产者
for i in 0..3 {
let tx = tx.clone();
thread::spawn(move || {
for j in 0..3 {
tx.send((i, j)).unwrap();
}
});
}
drop(tx);
// 单个消费者
while let Ok((producer, item)) = rx.recv() {
println!("生产者 {} 产出 {}", producer, item);
}
}
工作窃取模式(crossbeam)
// Cargo.toml: crossbeam = "0.8"
use crossbeam::channel;
use std::thread;
fn main() {
let (tx, rx) = channel::unbounded();
// 分发任务
for i in 0..10 {
tx.send(i).unwrap();
}
drop(tx);
// 多个 worker 从同一队列取任务
let handles: Vec<_> = (0..4).map(|id| {
let rx = rx.clone();
thread::spawn(move || {
while let Ok(task) = rx.recv() {
println!("Worker {} 处理任务 {}", id, task);
}
})
}).collect();
for h in handles {
h.join().unwrap();
}
}
总结对比
┌────────────────┬─────────────────┬─────────────────┬─────────────────┐
│ 方案 │ 适用场景 │ 性能 │ 复杂度 │
├────────────────┼─────────────────┼─────────────────┼─────────────────┤
│ thread::spawn │ 少量长期任务 │ 中等 │ 低 │
│ mpsc::channel │ 生产者-消费者 │ 中等 │ 低 │
│ Arc<Mutex<T>> │ 共享可变状态 │ 中等 │ 中 │
│ Arc<RwLock<T>> │ 读多写少 │ 较高 │ 中 │
│ Atomic* │ 简单计数/标志 │ 高 │ 中 │
│ Rayon │ 数据并行 │ 高 │ 低 │
│ crossbeam │ 复杂并发 │ 高 │ 中 │
└────────────────┴─────────────────┴─────────────────┴─────────────────┘
黄金法则:优先消息传递,必要时再用共享状态;优先用高级抽象(Rayon),避免手动管理线程。
多进程
核心概念总览
┌─────────────────────────────────────────────────────────────────────────┐
│ Rust 多进程体系 │
├─────────────────────────────────────────────────────────────────────────┤
│ std::process::Command → 创建和管理子进程 │
│ std::process::Child → 子进程句柄 │
│ std::process::Stdio → 标准输入/输出/错误配置 │
└─────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 管道 │ │ 共享内存 │ │ Socket/文件 │
│ (Pipe) │ │ (SharedMem) │ │ (IPC) │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ 父子进程通信 │ │ 高性能数据 │ │ 任意进程通信 │
│ 简单易用 │ │ 交换 │ │ 灵活通用 │
└──────────────┘ └──────────────┘ └──────────────┘
创建子进程
简单执行命令
use std::process::Command;
fn main() {
// 最简单的方式:执行并等待
let status = Command::new("echo")
.arg("Hello, Rust!")
.status()
.expect("执行失败");
println!("退出状态: {}", status);
println!("成功: {}", status.success());
// 获取退出码
if let Some(code) = status.code() {
println!("退出码: {}", code);
}
}
获取输出
use std::process::Command;
fn main() {
// output() 捕获 stdout 和 stderr
let output = Command::new("ls")
.arg("-la")
.arg("/tmp")
.output()
.expect("执行失败");
println!("状态: {}", output.status);
println!("stdout:\n{}", String::from_utf8_lossy(&output.stdout));
println!("stderr:\n{}", String::from_utf8_lossy(&output.stderr));
}
命令构建详解
use std::process::Command;
use std::collections::HashMap;
use std::path::Path;
fn main() {
let mut cmd = Command::new("python3");
// 添加参数
cmd.arg("-c")
.arg("import os; print(os.getcwd())")
// 或批量添加
.args(["-v", "--version"]);
// 设置环境变量
cmd.env("MY_VAR", "my_value")
.env("PATH", "/usr/bin:/bin")
// 清除所有环境变量后添加
.env_clear()
.envs([("KEY1", "VAL1"), ("KEY2", "VAL2")]);
// 设置工作目录
cmd.current_dir("/tmp");
// 执行
let output = cmd.output().expect("失败");
println!("{}", String::from_utf8_lossy(&output.stdout));
}
Stdio 控制
重定向配置
use std::process::{Command, Stdio};
use std::fs::File;
fn main() {
// ============ Stdio 选项 ============
// Stdio::inherit() - 继承父进程(默认)
// Stdio::piped() - 创建管道
// Stdio::null() - 丢弃(/dev/null)
// Stdio::from(file) - 重定向到文件
// 示例:静默执行
let status = Command::new("ls")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.unwrap();
// 示例:输出到文件
let file = File::create("/tmp/output.txt").unwrap();
let status = Command::new("echo")
.arg("写入文件")
.stdout(Stdio::from(file))
.status()
.unwrap();
// 示例:从文件读取输入
let input_file = File::open("/etc/hosts").unwrap();
let output = Command::new("head")
.arg("-n5")
.stdin(Stdio::from(input_file))
.output()
.unwrap();
println!("{}", String::from_utf8_lossy(&output.stdout));
}
管道通信
use std::process::{Command, Stdio};
use std::io::{Write, BufRead, BufReader};
fn main() {
// 创建可交互的子进程
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("启动失败");
// 获取 stdin 句柄
let mut stdin = child.stdin.take().expect("获取 stdin 失败");
// 在单独线程写入(避免死锁)
std::thread::spawn(move || {
stdin.write_all(b"Hello\nWorld\n").unwrap();
// stdin 在这里被 drop,发送 EOF
});
// 读取 stdout
let stdout = child.stdout.take().expect("获取 stdout 失败");
let reader = BufReader::new(stdout);
for line in reader.lines() {
println!("收到: {}", line.unwrap());
}
// 等待子进程结束
let status = child.wait().unwrap();
println!("子进程退出: {}", status);
}
进程管道链
命令管道(类似 shell |)
use std::process::{Command, Stdio};
fn main() {
// 模拟: ls -la | grep ".rs" | head -5
// 第一个命令
let ls = Command::new("ls")
.arg("-la")
.stdout(Stdio::piped())
.spawn()
.unwrap();
// 第二个命令,stdin 连接到第一个的 stdout
let grep = Command::new("grep")
.arg(".rs")
.stdin(Stdio::from(ls.stdout.unwrap()))
.stdout(Stdio::piped())
.spawn()
.unwrap();
// 第三个命令
let head = Command::new("head")
.arg("-5")
.stdin(Stdio::from(grep.stdout.unwrap()))
.output()
.unwrap();
println!("{}", String::from_utf8_lossy(&head.stdout));
}
封装管道函数
use std::process::{Command, Stdio, Child};
use std::io::Result;
fn pipe_commands(commands: Vec<Vec<&str>>) -> Result<String> {
let mut prev_stdout: Option<std::process::ChildStdout> = None;
let mut children: Vec<Child> = Vec::new();
for (i, cmd_args) in commands.iter().enumerate() {
let (cmd, args) = cmd_args.split_first().unwrap();
let mut command = Command::new(cmd);
command.args(args);
// 设置 stdin
if let Some(stdout) = prev_stdout.take() {
command.stdin(Stdio::from(stdout));
}
// 非最后一个命令,stdout 用管道
if i < commands.len() - 1 {
command.stdout(Stdio::piped());
}
let child = command.spawn()?;
prev_stdout = child.stdout;
children.push(child);
}
// 等待所有子进程
let mut final_output = String::new();
if let Some(last) = children.last_mut() {
let output = last.wait_with_output()?;
final_output = String::from_utf8_lossy(&output.stdout).to_string();
}
Ok(final_output)
}
fn main() {
let result = pipe_commands(vec![
vec!["echo", "hello\nworld\nrust"],
vec!["grep", "o"],
vec!["wc", "-l"],
]).unwrap();
println!("结果: {}", result.trim());
}
子进程管理
Child 句柄操作
use std::process::{Command, Stdio};
use std::time::Duration;
use std::thread;
fn main() {
let mut child = Command::new("sleep")
.arg("10")
.spawn()
.expect("启动失败");
println!("子进程 PID: {}", child.id());
// 非阻塞检查状态
match child.try_wait() {
Ok(Some(status)) => println!("已退出: {}", status),
Ok(None) => println!("仍在运行"),
Err(e) => println!("错误: {}", e),
}
// 等待一段时间后强制终止
thread::sleep(Duration::from_secs(2));
child.kill().expect("终止失败");
println!("已发送 SIGKILL");
// 必须 wait 回收资源(避免僵尸进程)
let status = child.wait().expect("等待失败");
println!("最终状态: {}", status);
}
超时执行
use std::process::Command;
use std::time::{Duration, Instant};
use std::thread;
fn run_with_timeout(cmd: &str, args: &[&str], timeout: Duration)
-> Result<String, String>
{
let mut child = Command::new(cmd)
.args(args)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.map_err(|e| e.to_string())?;
let start = Instant::now();
loop {
match child.try_wait() {
Ok(Some(status)) => {
let output = child.wait_with_output().unwrap();
if status.success() {
return Ok(String::from_utf8_lossy(&output.stdout).to_string());
} else {
return Err(String::from_utf8_lossy(&output.stderr).to_string());
}
}
Ok(None) => {
if start.elapsed() > timeout {
child.kill().ok();
child.wait().ok();
return Err("执行超时".to_string());
}
thread::sleep(Duration::from_millis(100));
}
Err(e) => return Err(e.to_string()),
}
}
}
fn main() {
match run_with_timeout("sleep", &["5"], Duration::from_secs(2)) {
Ok(output) => println!("输出: {}", output),
Err(e) => println!("错误: {}", e),
}
}
进程间通信(IPC)
匿名管道(父子进程)
// 上面的 Stdio::piped() 示例即是匿名管道
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader, Write};
fn main() {
let mut child = Command::new("sh")
.arg("-c")
.arg("read line; echo \"子进程收到: $line\"")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
// 写入子进程
if let Some(ref mut stdin) = child.stdin {
writeln!(stdin, "Hello from parent").unwrap();
}
child.stdin.take(); // 关闭 stdin
// 读取子进程输出
if let Some(stdout) = child.stdout.take() {
for line in BufReader::new(stdout).lines() {
println!("父进程收到: {}", line.unwrap());
}
}
child.wait().unwrap();
}
命名管道(FIFO)- Unix
#[cfg(unix)]
mod unix_fifo {
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::process::Command;
pub fn demo() {
let fifo_path = "/tmp/my_fifo";
// 创建命名管道
Command::new("mkfifo")
.arg(fifo_path)
.status()
.ok();
// 写者进程
let writer = std::thread::spawn(move || {
let mut file = OpenOptions::new()
.write(true)
.open(fifo_path)
.unwrap();
file.write_all(b"Hello via FIFO").unwrap();
});
// 读者进程
let reader = std::thread::spawn(move || {
let mut file = File::open(fifo_path).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
println!("读取: {}", buf);
});
writer.join().unwrap();
reader.join().unwrap();
// 清理
std::fs::remove_file(fifo_path).ok();
}
}
共享内存
// Cargo.toml: shared_memory = "0.12"
use shared_memory::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let shmem_name = "my_shared_mem";
// 创建共享内存
let shmem = ShmemConf::new()
.size(4096)
.os_id(shmem_name)
.create()?;
// 写入数据
unsafe {
let ptr = shmem.as_ptr();
std::ptr::copy_nonoverlapping(
b"Hello Shared Memory".as_ptr(),
ptr,
20
);
}
println!("共享内存已创建,其他进程可通过 '{}' 访问", shmem_name);
// 另一个进程打开已存在的共享内存
// let existing = ShmemConf::new().os_id(shmem_name).open()?;
Ok(())
}
Unix Domain Socket
#[cfg(unix)]
mod unix_socket {
use std::os::unix::net::{UnixListener, UnixStream};
use std::io::{Read, Write};
use std::thread;
pub fn demo() {
let socket_path = "/tmp/my_socket.sock";
// 清理旧的 socket 文件
std::fs::remove_file(socket_path).ok();
// 服务端
let server = thread::spawn(move || {
let listener = UnixListener::bind(socket_path).unwrap();
println!("服务端监听中...");
for stream in listener.incoming() {
match stream {
Ok(mut stream) => {
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf).unwrap();
println!("服务端收到: {}",
String::from_utf8_lossy(&buf[..n]));
stream.write_all(b"Hello from server").unwrap();
break;
}
Err(e) => eprintln!("错误: {}", e),
}
}
});
thread::sleep(std::time::Duration::from_millis(100));
// 客户端
let client = thread::spawn(move || {
let mut stream = UnixStream::connect(socket_path).unwrap();
stream.write_all(b"Hello from client").unwrap();
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf).unwrap();
println!("客户端收到: {}", String::from_utf8_lossy(&buf[..n]));
});
server.join().unwrap();
client.join().unwrap();
std::fs::remove_file(socket_path).ok();
}
}
IPC 方案对比
┌─────────────────┬──────────────┬──────────────┬────────────────────────┐
│ 方案 │ 性能 │ 使用范围 │ 特点 │
├─────────────────┼──────────────┼──────────────┼────────────────────────┤
│ 匿名管道 │ 高 │ 父子进程 │ 简单,单向 │
│ 命名管道(FIFO) │ 高 │ 任意进程 │ 文件系统可见 │
│ Unix Socket │ 高 │ 同一主机 │ 双向,支持多连接 │
│ TCP Socket │ 中 │ 跨网络 │ 通用,开销较大 │
│ 共享内存 │ 最高 │ 同一主机 │ 需要同步机制 │
│ 内存映射文件 │ 高 │ 同一主机 │ 持久化,大数据 │
│ 消息队列 │ 中 │ 同一主机 │ 解耦,异步 │
└─────────────────┴──────────────┴──────────────┴────────────────────────┘
信号处理
使用 ctrlc 库
// Cargo.toml: ctrlc = "3.4"
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
fn main() {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
println!("\n收到 Ctrl+C,正在优雅退出...");
r.store(false, Ordering::SeqCst);
}).expect("设置处理器失败");
println!("运行中... 按 Ctrl+C 退出");
while running.load(Ordering::SeqCst) {
std::thread::sleep(std::time::Duration::from_millis(100));
// 做一些工作...
}
println!("清理完成,退出");
}
使用 signal-hook 库
// Cargo.toml: signal-hook = "0.3"
use signal_hook::{consts::*, iterator::Signals};
use std::thread;
fn main() {
let mut signals = Signals::new(&[
SIGTERM,
SIGINT,
SIGHUP,
SIGQUIT,
]).unwrap();
// 在单独线程处理信号
thread::spawn(move || {
for sig in signals.forever() {
match sig {
SIGTERM | SIGINT => {
println!("收到终止信号,退出");
std::process::exit(0);
}
SIGHUP => {
println!("收到 SIGHUP,重新加载配置");
}
_ => unreachable!(),
}
}
});
// 主线程工作
loop {
println!("工作中...");
thread::sleep(std::time::Duration::from_secs(1));
}
}
发送信号给子进程
#[cfg(unix)]
fn send_signal_demo() {
use std::process::Command;
use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
let child = Command::new("sleep")
.arg("100")
.spawn()
.unwrap();
let pid = Pid::from_raw(child.id() as i32);
// 发送 SIGTERM
signal::kill(pid, Signal::SIGTERM).unwrap();
println!("已发送 SIGTERM 给 PID {}", child.id());
}
守护进程
使用 daemonize 库
// Cargo.toml: daemonize = "0.5"
#[cfg(unix)]
fn daemonize_demo() {
use daemonize::Daemonize;
use std::fs::File;
let stdout = File::create("/tmp/daemon.out").unwrap();
let stderr = File::create("/tmp/daemon.err").unwrap();
let daemonize = Daemonize::new()
.pid_file("/tmp/daemon.pid")
.chown_pid_file(true)
.working_directory("/tmp")
.user("nobody")
.group("daemon")
.stdout(stdout)
.stderr(stderr)
.privileged_action(|| "执行特权操作");
match daemonize.start() {
Ok(_) => {
println!("守护进程启动成功");
// 守护进程主循环
loop {
std::thread::sleep(std::time::Duration::from_secs(10));
}
}
Err(e) => eprintln!("守护进程启动失败: {}", e),
}
}
进程池模式
use std::process::{Command, Child, Stdio};
use std::collections::VecDeque;
use std::io::Write;
struct ProcessPool {
workers: Vec<Child>,
max_workers: usize,
}
impl ProcessPool {
fn new(max_workers: usize) -> Self {
Self {
workers: Vec::new(),
max_workers,
}
}
fn spawn_worker(&mut self, task: &str) -> std::io::Result<()> {
// 清理已完成的 worker
self.workers.retain_mut(|child| {
match child.try_wait() {
Ok(Some(_)) => false, // 已结束,移除
_ => true, // 仍在运行,保留
}
});
// 等待有空闲槽位
while self.workers.len() >= self.max_workers {
std::thread::sleep(std::time::Duration::from_millis(100));
self.workers.retain_mut(|child| {
child.try_wait().map(|s| s.is_none()).unwrap_or(true)
});
}
// 启动新 worker
let child = Command::new("sh")
.arg("-c")
.arg(task)
.spawn()?;
self.workers.push(child);
Ok(())
}
fn wait_all(&mut self) {
for child in &mut self.workers {
child.wait().ok();
}
self.workers.clear();
}
}
fn main() {
let mut pool = ProcessPool::new(4);
// 提交任务
for i in 0..10 {
let task = format!("echo '任务 {}'; sleep 1", i);
pool.spawn_worker(&task).unwrap();
println!("提交任务 {}", i);
}
pool.wait_all();
println!("所有任务完成");
}
实用工具函数
use std::process::{Command, Output, ExitStatus};
use std::io::{Result, Error, ErrorKind};
/// 执行命令并返回 stdout,失败时返回 stderr
pub fn exec(cmd: &str) -> Result<String> {
let output = if cfg!(target_os = "windows") {
Command::new("cmd").args(["/C", cmd]).output()?
} else {
Command::new("sh").args(["-c", cmd]).output()?
};
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
} else {
Err(Error::new(
ErrorKind::Other,
String::from_utf8_lossy(&output.stderr).to_string()
))
}
}
/// 检查命令是否存在
pub fn command_exists(cmd: &str) -> bool {
Command::new("which")
.arg(cmd)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
/// 获取当前进程信息
pub fn process_info() {
println!("PID: {}", std::process::id());
println!("当前目录: {:?}", std::env::current_dir().unwrap());
println!("可执行文件: {:?}", std::env::current_exe().unwrap());
println!("参数: {:?}", std::env::args().collect::<Vec<_>>());
}
fn main() {
// 使用示例
match exec("uname -a") {
Ok(output) => println!("系统信息: {}", output),
Err(e) => eprintln!("错误: {}", e),
}
println!("git 存在: {}", command_exists("git"));
process_info();
}
总结对比
┌─────────────────────────────────────────────────────────────────────────┐
│ 多线程 vs 多进程 选择指南 │
├─────────────────┬───────────────────────┬───────────────────────────────┤
│ 维度 │ 多线程 │ 多进程 │
├─────────────────┼───────────────────────┼───────────────────────────────┤
│ 内存共享 │ 直接共享 │ 需要 IPC │
│ 创建开销 │ 小 │ 大 │
│ 通信开销 │ 小 │ 大 │
│ 隔离性 │ 弱(崩溃影响整体) │ 强(独立地址空间) │
│ 调试难度 │ 高(竞态条件) │ 中 │
│ 适用场景 │ CPU 密集、共享状态 │ 隔离执行、调用外部程序 │
│ Rust 安全性 │ 编译期保证 │ 运行时 │
└─────────────────┴───────────────────────┴───────────────────────────────┘
推荐库
| 用途 | 库 |
|---|---|
| 基础进程 | std::process |
| Unix 系统调用 | nix |
| 信号处理 | signal-hook, ctrlc |
| 共享内存 | shared_memory |
| 守护进程 | daemonize |
| 跨平台 IPC | ipc-channel |
选择原则:优先使用多线程处理 CPU 密集任务;需要隔离、调用外部程序、或利用多核避免 GIL(其他语言)时使用多进程。
异步编程
核心概念总览
┌─────────────────────────────────────────────────────────────────┐
│ Rust 异步编程架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ async/await │ ← 语法糖(语言内置) │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Future Trait │ ← 核心抽象(标准库) │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Runtime │ ← 执行引擎(第三方:tokio/async-std) │
│ │ (Executor + │ │
│ │ Reactor) │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
关键点:Rust 只提供语法和抽象,不提供运行时!
标准库内置支持
Future Trait(核心)
// 标准库定义 (简化版)
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
pub enum Poll<T> {
Ready(T), // 已完成,返回结果
Pending, // 未完成,稍后再试
}
async/await 语法
// async fn 返回一个实现 Future 的匿名类型
async fn fetch_data() -> String {
// await 暂停执行,让出控制权
let response = make_request().await;
response.body
}
// 等价于(概念上):
fn fetch_data() -> impl Future<Output = String> {
async {
let response = make_request().await;
response.body
}
}
惰性执行特性
async fn hello() {
println!("Hello!");
}
fn main() {
let future = hello(); // ⚠️ 什么都不会发生!
// Future 是惰性的,必须被 poll 才会执行
}
为什么需要 Runtime
┌────────────────────────────────────────────────────────────┐
│ 异步运行时职责 │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Executor │ │ Reactor │ │
│ │ (执行器) │ │ (反应器) │ │
│ ├──────────────┤ ├──────────────┤ │
│ │ • 调度任务 │ │ • I/O 事件 │ │
│ │ • 调用 poll │ │ 监听 │ │
│ │ • 任务切换 │ │ • 唤醒任务 │ │
│ └──────────────┘ └──────────────┘ │
│ │ │ │
│ └───────┬───────────┘ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 异步任务队列 │ │
│ └────────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘
Tokio 详解
基础设置
# Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }
# 或按需选择
# tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "net", "io-util", "time"] }
入口点
// 方式1: 宏(推荐)
#[tokio::main]
async fn main() {
println!("Hello from tokio!");
}
// 方式2: 手动构建 Runtime
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
println!("Hello from tokio!");
});
}
// 方式3: 单线程运行时
#[tokio::main(flavor = "current_thread")]
async fn main() {
// 适合轻量级应用
}
任务生成 (spawn)
use tokio::task;
#[tokio::main]
async fn main() {
// 生成并发任务
let handle1 = task::spawn(async {
// 独立运行的任务
expensive_computation().await
});
let handle2 = task::spawn(async {
fetch_from_network().await
});
// 等待任务完成
let (result1, result2) = tokio::join!(handle1, handle2);
println!("Results: {:?}, {:?}", result1, result2);
}
核心异步原语
use tokio::time::{sleep, timeout, Duration};
use tokio::sync::{mpsc, Mutex, RwLock, oneshot};
// ═══════════════════════════════════════════
// 时间操作
// ═══════════════════════════════════════════
async fn time_examples() {
// 异步睡眠
sleep(Duration::from_secs(1)).await;
// 超时控制
let result = timeout(Duration::from_secs(5), slow_operation()).await;
match result {
Ok(value) => println!("完成: {}", value),
Err(_) => println!("超时!"),
}
}
// ═══════════════════════════════════════════
// 通道 (Channel)
// ═══════════════════════════════════════════
async fn channel_example() {
// MPSC: 多生产者单消费者
let (tx, mut rx) = mpsc::channel::<String>(100);
tokio::spawn(async move {
tx.send("Hello".to_string()).await.unwrap();
});
while let Some(msg) = rx.recv().await {
println!("收到: {}", msg);
}
// Oneshot: 一次性通道
let (tx, rx) = oneshot::channel::<i32>();
tx.send(42).unwrap();
let value = rx.await.unwrap();
}
// ═══════════════════════════════════════════
// 异步锁
// ═══════════════════════════════════════════
async fn lock_example() {
let data = Mutex::new(vec![1, 2, 3]);
{
let mut guard = data.lock().await;
guard.push(4);
} // 锁在这里释放
}
异步 I/O
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
// TCP 服务器示例
async fn tcp_server() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (socket, addr) = listener.accept().await?;
println!("新连接: {}", addr);
// 为每个连接生成独立任务
tokio::spawn(async move {
handle_connection(socket).await;
});
}
}
async fn handle_connection(mut socket: TcpStream) {
let mut buffer = [0u8; 1024];
loop {
let n = match socket.read(&mut buffer).await {
Ok(0) => return, // 连接关闭
Ok(n) => n,
Err(_) => return,
};
// Echo 回去
socket.write_all(&buffer[..n]).await.unwrap();
}
}
文件操作
use tokio::fs;
use tokio::io::{AsyncBufReadExt, BufReader};
async fn file_operations() -> std::io::Result<()> {
// 读取整个文件
let content = fs::read_to_string("config.txt").await?;
// 写入文件
fs::write("output.txt", "Hello, World!").await?;
// 逐行读取
let file = fs::File::open("data.txt").await?;
let reader = BufReader::new(file);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await? {
println!("{}", line);
}
Ok(())
}
并发控制模式
并发 vs 顺序执行
async fn concurrent_operations() {
// ❌ 顺序执行 - 总时间 = A + B + C
let a = fetch_a().await;
let b = fetch_b().await;
let c = fetch_c().await;
// ✅ 并发执行 - 总时间 = max(A, B, C)
let (a, b, c) = tokio::join!(
fetch_a(),
fetch_b(),
fetch_c()
);
// ✅ 竞争执行 - 返回最先完成的
tokio::select! {
result = fetch_a() => println!("A 先完成: {:?}", result),
result = fetch_b() => println!("B 先完成: {:?}", result),
}
}
Select 用法
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
async fn select_example() {
let (tx, mut rx) = mpsc::channel::<i32>(10);
loop {
tokio::select! {
// 优先级:按顺序检查
Some(msg) = rx.recv() => {
println!("收到消息: {}", msg);
}
_ = sleep(Duration::from_secs(1)) => {
println!("心跳...");
}
// 可取消
_ = tokio::signal::ctrl_c() => {
println!("收到退出信号");
break;
}
}
}
}
常见陷阱
// ═══════════════════════════════════════════
// ❌ 陷阱1: 在异步中使用 std::sync::Mutex
// ═══════════════════════════════════════════
// 错误:可能导致死锁
use std::sync::Mutex;
async fn bad_lock(data: &Mutex<Vec<i32>>) {
let mut guard = data.lock().unwrap();
some_async_fn().await; // ⚠️ 持有锁时 await
guard.push(1);
}
// 正确:使用 tokio::sync::Mutex 或减少锁持有时间
use tokio::sync::Mutex;
async fn good_lock(data: &Mutex<Vec<i32>>) {
let mut guard = data.lock().await;
guard.push(1);
} // 在 await 前释放锁
// ═══════════════════════════════════════════
// ❌ 陷阱2: 阻塞操作
// ═══════════════════════════════════════════
// 错误:阻塞整个执行器线程
async fn bad_blocking() {
std::thread::sleep(Duration::from_secs(10)); // ⚠️ 同步阻塞
std::fs::read_to_string("file.txt"); // ⚠️ 同步 I/O
}
// 正确:
async fn good_async() {
tokio::time::sleep(Duration::from_secs(10)).await;
tokio::fs::read_to_string("file.txt").await;
// 不可避免的阻塞操作
tokio::task::spawn_blocking(|| {
heavy_cpu_computation()
}).await;
}
// ═══════════════════════════════════════════
// ❌ 陷阱3: 忘记 .await
// ═══════════════════════════════════════════
async fn forget_await() {
let _ = async_operation(); // ⚠️ 编译通过但不执行!
async_operation().await; // ✅ 正确
}
为什么异步编程需要 Lock 和 Mutex
核心原因图解
┌─────────────────────────────────────────────────────────────────────┐
│ 为什么异步也需要锁? │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 原因1: Tokio 默认是多线程运行时 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Thread 1 Thread 2 Thread 3 │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │Task A │ │Task B │ │Task C │ │ │
│ │ └───┬───┘ └───┬───┘ └───┬───┘ │ │
│ │ │ │ │ │ │
│ │ └────────────────┼────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌────────────────┐ │ │
│ │ │ 共享数据 Vec │ ← 真正的并行访问! │ │
│ │ └────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 原因2: 即使单线程,await 点也会切换任务 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 时间线 ──────────────────────────────────────────────► │ │
│ │ │ │
│ │ Task A: [读取 x=5] ──await──────────────► [写入 x=6] │ │
│ │ ↓ │ │
│ │ Task B: [读取 x=5] [写入 x=10] │ │
│ │ │ │
│ │ 结果: x 最终是 6 还是 10?取决于执行顺序! │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
具体场景演示
场景1:多线程运行时
use std::sync::Arc;
use tokio::task;
// ❌ 错误:编译不通过 != Rust 保护你
#[tokio::main]
async fn wrong_example() {
let mut counter = 0; // 非线程安全
let handle1 = task::spawn(async {
counter += 1; // ❌ 无法捕获可变引用
});
let handle2 = task::spawn(async {
counter += 1; // ❌
});
}
// ✅ 正确:使用 Arc + Mutex
use tokio::sync::Mutex;
#[tokio::main]
async fn correct_example() {
let counter = Arc::new(Mutex::new(0));
let counter1 = Arc::clone(&counter);
let handle1 = task::spawn(async move {
let mut guard = counter1.lock().await;
*guard += 1;
});
let counter2 = Arc::clone(&counter);
let handle2 = task::spawn(async move {
let mut guard = counter2.lock().await;
*guard += 1;
});
handle1.await.unwrap();
handle2.await.unwrap();
println!("Counter: {}", *counter.lock().await); // 保证是 2
}
场景2:单线程但有逻辑竞争
use std::cell::RefCell;
use std::rc::Rc;
// 即使单线程,也有问题!
#[tokio::main(flavor = "current_thread")]
async fn single_thread_problem() {
let balance = Rc::new(RefCell::new(100)); // 银行账户余额
// 模拟两个并发的取款操作
let b1 = Rc::clone(&balance);
let withdraw1 = async move {
let current = *b1.borrow(); // 读取: 100
// ⚠️ 在这个 await 期间,另一个任务可能修改 balance
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
if current >= 80 {
*b1.borrow_mut() = current - 80; // 写入: 20
}
};
let b2 = Rc::clone(&balance);
let withdraw2 = async move {
let current = *b2.borrow(); // 也读取到: 100
tokio::time::sleep(tokio::time::Duration::from_millis(5)).await;
if current >= 80 {
*b2.borrow_mut() = current - 80; // 写入: 20
}
};
tokio::join!(withdraw1, withdraw2);
// ❌ 结果: balance = 20,但实际取了 160!
// 这就是 Race Condition(竞态条件)
}
关键概念区分
┌────────────────────────────────────────────────────────────────────┐
│ 两种不同的"竞争"问题 │
├──────────────────────────┬─────────────────────────────────────────┤
│ Data Race │ Race Condition │
│ (数据竞争) │ (竞态条件) │
├──────────────────────────┼─────────────────────────────────────────┤
│ • 多线程同时访问同一内存 │ • 操作的执行顺序不确定 │
│ • 至少一个是写操作 │ • 导致逻辑结果不正确 │
│ • 没有同步机制 │ • 单线程也可能发生 │
├──────────────────────────┼─────────────────────────────────────────┤
│ Rust 编译器完全阻止 ✅ │ Rust 编译器无法检测 ⚠️ │
├──────────────────────────┼─────────────────────────────────────────┤
│ 需要:Mutex, RwLock │ 需要:Mutex + 正确的锁粒度 │
└──────────────────────────┴─────────────────────────────────────────┘
await 的危险性
// 展示 await 如何"撕裂"原子操作
async fn transfer(from: &Mutex<i32>, to: &Mutex<i32>, amount: i32) {
// ❌ 危险的写法
let mut from_guard = from.lock().await;
*from_guard -= amount;
drop(from_guard); // 释放锁
// ⚠️ 这里有个"窗口期"!
// 钱已经从 from 扣除,但还没加到 to
// 如果此时系统崩溃或其他任务读取,数据不一致
do_something_slow().await; // 💥 更糟糕!
let mut to_guard = to.lock().await;
*to_guard += amount;
}
// ✅ 更安全的写法:减少 await 跨越的锁操作
async fn safe_transfer(from: &Mutex<i32>, to: &Mutex<i32>, amount: i32) {
// 方案1: 同时持有两把锁
let (mut from_guard, mut to_guard) = tokio::join!(
from.lock(),
to.lock()
);
*from_guard -= amount;
*to_guard += amount;
// 同时释放
}
何时需要/不需要锁
┌─────────────────────────────────────────────────────────────────┐
│ 决策流程图 │
└─────────────────────────────────────────────────────────────────┘
有共享可变状态吗?
│
┌───────────┴───────────┐
▼ ▼
YES NO
│ │
▼ ▼
跨多个任务吗? 不需要锁 ✅
│ (每个任务有自己的数据)
┌─────────┴─────────┐
▼ ▼
YES NO
│ │
▼ ▼
需要锁 🔒 可能不需要
│ (但要小心 await)
│
▼
多线程运行时?
│
┌──┴──┐
▼ ▼
YES NO (current_thread)
│ │
▼ ▼
Arc + Rc + RefCell 可行
Mutex 但仍建议用 Mutex
不需要锁的情况
// 1. 消息传递代替共享状态
use tokio::sync::mpsc;
async fn no_lock_needed() {
let (tx, mut rx) = mpsc::channel(100);
// 生产者
tokio::spawn(async move {
tx.send(42).await.unwrap();
});
// 消费者:独占所有权,无需锁
while let Some(value) = rx.recv().await {
println!("{}", value);
}
}
// 2. 任务内部状态:无共享
async fn process_item(item: String) {
let mut local_data = Vec::new(); // 任务私有
local_data.push(item);
// 不需要锁
}
// 3. 只读共享
async fn read_only_sharing() {
let config = Arc::new(Config::load()); // 不可变
let c1 = Arc::clone(&config);
tokio::spawn(async move {
println!("{}", c1.name); // 只读,无需锁
});
}
std::sync::Mutex vs tokio::sync::Mutex
// ═══════════════════════════════════════════════════════════════
// 关键区别
// ═══════════════════════════════════════════════════════════════
// std::sync::Mutex - 阻塞式
let std_mutex = std::sync::Mutex::new(0);
let guard = std_mutex.lock().unwrap(); // 🔴 阻塞当前线程!
// tokio::sync::Mutex - 异步式
let tokio_mutex = tokio::sync::Mutex::new(0);
let guard = tokio_mutex.lock().await; // 🟢 让出执行权,不阻塞线程
// ═══════════════════════════════════════════════════════════════
// 什么时候用哪个?
// ═══════════════════════════════════════════════════════════════
// ✅ 用 std::sync::Mutex:锁内无 await,且锁持有时间很短
async fn use_std_mutex(data: &std::sync::Mutex<Vec<i32>>) {
{
let mut guard = data.lock().unwrap();
guard.push(1); // 快速操作
} // 立即释放
do_async_work().await;
}
// ✅ 用 tokio::sync::Mutex:锁内有 await,或锁持有时间长
async fn use_tokio_mutex(data: &tokio::sync::Mutex<Vec<i32>>) {
let mut guard = data.lock().await;
// 可以在持有锁时 await(虽然通常不推荐)
let result = fetch_data().await;
guard.push(result);
}
| 误解 | 事实 |
|---|---|
| "异步是单线程的" | Tokio 默认多线程,任务可能并行 |
| "单线程不需要锁" | await 点会切换任务,造成竞态条件 |
| "用 channel 就不需要锁" | channel 内部也用了锁,但封装好了 |
| "锁会影响性能" | 正确使用锁的开销很小,数据损坏更可怕 |
黄金法则:如果多个任务需要修改同一数据,就需要同步机制(Mutex/Channel/Atomic)
总结对比表
| 特性 | 标准库 | Tokio |
|---|---|---|
| async/await | ✅ | ✅ |
| Future trait | ✅ | ✅ |
| Runtime | ❌ | ✅ 多线程/单线程 |
| 异步 I/O | ❌ | ✅ TCP/UDP/Unix |
| 定时器 | ❌ | ✅ sleep/timeout |
| 通道 | ❌ | ✅ mpsc/broadcast/watch |
| 异步锁 | ❌ | ✅ Mutex/RwLock/Semaphore |
| 文件 I/O | ❌ | ✅ |
选择建议: - 网络服务/高并发 → Tokio - 简单异步需求 → async-std (更简单的 API) - 嵌入式/WASM → smol 或自定义
网络编程
核心概念架构
┌─────────────────────────────────────────────────────────────────┐
│ Rust 网络编程生态 │
├─────────────────────────────────────────────────────────────────┤
│ 应用层框架 │ actix-web │ axum │ rocket │ warp │ hyper │
├─────────────────────────────────────────────────────────────────┤
│ 异步运行时 │ tokio │ async-std │ smol │
├─────────────────────────────────────────────────────────────────┤
│ 标准库 │ std::net (同步阻塞式) │
├─────────────────────────────────────────────────────────────────┤
│ 协议层 │ TCP │ UDP │ HTTP │ WebSocket │
└─────────────────────────────────────────────────────────────────┘
标准库同步网络编程 (std::net)
TCP 服务端/客户端
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;
// ============ TCP 服务端 ============
fn tcp_server() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Server listening on port 8080");
for stream in listener.incoming() {
let stream = stream?;
// 为每个连接创建新线程
thread::spawn(move || {
handle_client(stream).unwrap_or_else(|e| eprintln!("Error: {}", e));
});
}
Ok(())
}
fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
let mut buffer = [0u8; 1024];
loop {
let bytes_read = stream.read(&mut buffer)?;
if bytes_read == 0 { break; } // 连接关闭
// Echo: 原样返回数据
stream.write_all(&buffer[..bytes_read])?;
}
Ok(())
}
// ============ TCP 客户端 ============
fn tcp_client() -> std::io::Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:8080")?;
stream.write_all(b"Hello, Server!")?;
let mut buffer = [0u8; 1024];
let n = stream.read(&mut buffer)?;
println!("Received: {}", String::from_utf8_lossy(&buffer[..n]));
Ok(())
}
UDP 通信
use std::net::UdpSocket;
// UDP 服务端
fn udp_server() -> std::io::Result<()> {
let socket = UdpSocket::bind("127.0.0.1:8080")?;
let mut buf = [0u8; 1024];
loop {
let (len, src_addr) = socket.recv_from(&mut buf)?;
println!("Received {} bytes from {}", len, src_addr);
// 回复
socket.send_to(&buf[..len], src_addr)?;
}
}
// UDP 客户端
fn udp_client() -> std::io::Result<()> {
let socket = UdpSocket::bind("0.0.0.0:0")?; // 随机端口
socket.connect("127.0.0.1:8080")?;
socket.send(b"Hello UDP!")?;
let mut buf = [0u8; 1024];
let len = socket.recv(&mut buf)?;
println!("Response: {}", String::from_utf8_lossy(&buf[..len]));
Ok(())
}
异步网络编程
Cargo.toml 配置
[dependencies]
tokio = { version = "1", features = ["full"] }
异步 TCP 服务端
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Async server listening on 8080");
loop {
let (socket, addr) = listener.accept().await?;
println!("New connection from: {}", addr);
// 异步任务处理连接(无需线程)
tokio::spawn(async move {
handle_connection(socket).await;
});
}
}
async fn handle_connection(mut socket: TcpStream) {
let mut buffer = vec![0u8; 1024];
loop {
match socket.read(&mut buffer).await {
Ok(0) => break, // 连接关闭
Ok(n) => {
if socket.write_all(&buffer[..n]).await.is_err() {
break;
}
}
Err(_) => break,
}
}
}
异步 TCP 客户端
use tokio::net::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
stream.write_all(b"Hello Async!").await?;
let mut buffer = vec![0u8; 1024];
let n = stream.read(&mut buffer).await?;
println!("Received: {}", String::from_utf8_lossy(&buffer[..n]));
Ok(())
}
HTTP 客户端
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
use reqwest;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Post {
id: Option<u32>,
title: String,
body: String,
#[serde(rename = "userId")]
user_id: u32,
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
// GET 请求
let response = reqwest::get("https://jsonplaceholder.typicode.com/posts/1")
.await?
.json::<Post>()
.await?;
println!("GET: {:?}", response);
// POST 请求
let new_post = Post {
id: None,
title: "foo".into(),
body: "bar".into(),
user_id: 1,
};
let client = reqwest::Client::new();
let res = client
.post("https://jsonplaceholder.typicode.com/posts")
.json(&new_post)
.send()
.await?;
println!("POST Status: {}", res.status());
Ok(())
}
Web 服务器框架
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use axum::{
routing::{get, post},
Router, Json, extract::Path,
http::StatusCode,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
#[derive(Serialize)]
struct User {
id: u64,
name: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
}
// GET /
async fn root() -> &'static str {
"Hello, World!"
}
// GET /users/:id
async fn get_user(Path(id): Path<u64>) -> Json<User> {
Json(User { id, name: "Alice".into() })
}
// POST /users
async fn create_user(Json(payload): Json<CreateUser>) -> (StatusCode, Json<User>) {
let user = User {
id: 1,
name: payload.name,
};
(StatusCode::CREATED, Json(user))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(root))
.route("/users/:id", get(get_user))
.route("/users", post(create_user));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Server running at http://{}", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
WebSocket
[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.21"
futures-util = "0.3"
use tokio::net::TcpListener;
use tokio_tungstenite::accept_async;
use futures_util::{StreamExt, SinkExt};
#[tokio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:9000").await.unwrap();
println!("WebSocket server on ws://127.0.0.1:9000");
while let Ok((stream, addr)) = listener.accept().await {
tokio::spawn(async move {
let ws_stream = accept_async(stream).await.unwrap();
println!("WebSocket connection from: {}", addr);
let (mut write, mut read) = ws_stream.split();
// Echo 服务
while let Some(msg) = read.next().await {
if let Ok(msg) = msg {
if msg.is_text() || msg.is_binary() {
write.send(msg).await.unwrap();
}
}
}
});
}
}
关键对比总结
| 特性 | std::net | tokio | async-std |
|---|---|---|---|
| 模型 | 同步阻塞 | 异步非阻塞 | 异步非阻塞 |
| 并发方式 | 多线程 | 任务(Task) | 任务(Task) |
| 性能 | 中等 | 高 | 高 |
| 复杂度 | 简单 | 中等 | 中等 |
| 适用场景 | 简单工具/学习 | 高并发服务 | 高并发服务 |
生态库推荐
| 用途 | 推荐库 |
|---|---|
| HTTP 客户端 | reqwest |
| HTTP 服务器 | axum, actix-web |
| WebSocket | tokio-tungstenite |
| gRPC | tonic |
| 底层 HTTP | hyper |
| DNS 解析 | trust-dns |
| TLS | rustls, native-tls |
文件操作
核心概念架构
┌─────────────────────────────────────────────────────────────────┐
│ Rust 文件操作体系 │
├─────────────────────────────────────────────────────────────────┤
│ 路径处理 │ std::path::Path │ std::path::PathBuf │
├─────────────────────────────────────────────────────────────────┤
│ 文件系统 │ std::fs (同步) │ tokio::fs (异步) │
├─────────────────────────────────────────────────────────────────┤
│ I/O Traits │ Read │ Write │ Seek │ BufRead │ BufWriter │
├─────────────────────────────────────────────────────────────────┤
│ 高级封装 │ 读: read_to_string │ 写: write │ 追加: append │
└─────────────────────────────────────────────────────────────────┘
快速文件读写
use std::fs;
fn main() -> std::io::Result<()> {
// ============ 读取文件 ============
// 1. 一次性读取为 String(最常用)
let content = fs::read_to_string("config.txt")?;
println!("{}", content);
// 2. 一次性读取为字节 Vec<u8>(二进制文件)
let bytes = fs::read("image.png")?;
println!("File size: {} bytes", bytes.len());
// ============ 写入文件 ============
// 3. 写入字符串(覆盖已有内容)
fs::write("output.txt", "Hello, Rust!")?;
// 4. 写入字节数据
fs::write("data.bin", &[0x48, 0x65, 0x6c, 0x6c, 0x6f])?;
Ok(())
}
File 结构体操作
打开和创建文件
use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
// ============ 基本打开方式 ============
// 只读打开(文件必须存在)
let file = File::open("input.txt")?;
// 创建/覆盖写入(文件不存在则创建)
let file = File::create("output.txt")?;
// ============ OpenOptions 精细控制 ============
// 追加模式
let mut file = OpenOptions::new()
.append(true)
.open("log.txt")?;
// 读写模式
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open("data.txt")?;
// 创建新文件(如已存在则报错)
let file = OpenOptions::new()
.write(true)
.create_new(true)
.open("new_file.txt")?;
// 完整选项示例
let mut file = OpenOptions::new()
.read(true) // 可读
.write(true) // 可写
.create(true) // 不存在则创建
.truncate(false) // 不清空原内容
.open("config.txt")?;
Ok(())
}
读取操作
use std::fs::File;
use std::io::{Read, BufRead, BufReader};
fn main() -> std::io::Result<()> {
// ============ 方式1: 读取到 String ============
let mut file = File::open("data.txt")?;
let mut content = String::new();
file.read_to_string(&mut content)?;
// ============ 方式2: 读取到 Vec<u8> ============
let mut file = File::open("data.bin")?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
// ============ 方式3: 读取固定大小块 ============
let mut file = File::open("large.bin")?;
let mut chunk = [0u8; 1024];
loop {
let bytes_read = file.read(&mut chunk)?;
if bytes_read == 0 { break; }
// 处理 chunk[..bytes_read]
println!("Read {} bytes", bytes_read);
}
// ============ 方式4: 按行读取(推荐大文件)============
let file = File::open("log.txt")?;
let reader = BufReader::new(file);
for line in reader.lines() {
let line = line?;
println!("{}", line);
}
// ============ 方式5: 按行读取(保留换行符)============
let file = File::open("data.txt")?;
let reader = BufReader::new(file);
for line in reader.split(b'\n') {
let line = line?;
// line: Vec<u8>
}
Ok(())
}
写入操作
use std::fs::File;
use std::io::{Write, BufWriter};
fn main() -> std::io::Result<()> {
// ============ 基本写入 ============
let mut file = File::create("output.txt")?;
// 写入字节
file.write_all(b"Hello, World!\n")?;
// 格式化写入
writeln!(file, "Number: {}", 42)?;
writeln!(file, "Name: {}", "Alice")?;
// 确保数据写入磁盘
file.sync_all()?;
// ============ 缓冲写入(推荐大量写入)============
let file = File::create("large_output.txt")?;
let mut writer = BufWriter::new(file);
for i in 0..10000 {
writeln!(writer, "Line {}", i)?;
}
writer.flush()?; // 刷新缓冲区
// ============ 自定义缓冲区大小 ============
let file = File::create("data.txt")?;
let mut writer = BufWriter::with_capacity(64 * 1024, file); // 64KB
Ok(())
}
文件指针定位 (Seek)
use std::fs::OpenOptions;
use std::io::{Read, Write, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open("data.txt")?;
// 移动到文件开头
file.seek(SeekFrom::Start(0))?;
// 移动到文件末尾
file.seek(SeekFrom::End(0))?;
// 相对当前位置移动
file.seek(SeekFrom::Current(10))?; // 向前10字节
file.seek(SeekFrom::Current(-5))?; // 向后5字节
// 获取当前位置
let pos = file.stream_position()?;
println!("Current position: {}", pos);
// 回到开头并读取
file.rewind()?; // 等价于 seek(SeekFrom::Start(0))
Ok(())
}
路径操作 (Path/PathBuf)
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
fn main() {
// ============ 创建路径 ============
// Path: 不可变引用(类似 &str)
let path = Path::new("/home/user/file.txt");
// PathBuf: 可变拥有(类似 String)
let mut path_buf = PathBuf::from("/home/user");
path_buf.push("documents");
path_buf.push("file.txt");
println!("{}", path_buf.display()); // /home/user/documents/file.txt
// ============ 路径组件提取 ============
let path = Path::new("/home/user/document.tar.gz");
println!("文件名: {:?}", path.file_name()); // Some("document.tar.gz")
println!("文件干名: {:?}", path.file_stem()); // Some("document.tar")
println!("扩展名: {:?}", path.extension()); // Some("gz")
println!("父目录: {:?}", path.parent()); // Some("/home/user")
// ============ 路径判断 ============
let path = Path::new("./config.toml");
println!("是否绝对路径: {}", path.is_absolute()); // false
println!("是否相对路径: {}", path.is_relative()); // true
println!("是否存在: {}", path.exists());
println!("是文件: {}", path.is_file());
println!("是目录: {}", path.is_dir());
println!("是符号链接: {}", path.is_symlink());
// ============ 路径操作 ============
let mut path = PathBuf::from("/home/user/file.txt");
// 修改文件名
path.set_file_name("new_file.txt");
// 修改扩展名
path.set_extension("md");
// 拼接路径
let full_path = Path::new("/home").join("user").join("file.txt");
// ============ 规范化路径 ============
let path = Path::new("/home/user/../user/./file.txt");
// 获取规范路径(解析 .. 和 .)
if let Ok(canonical) = path.canonicalize() {
println!("规范路径: {}", canonical.display());
}
// ============ 遍历路径组件 ============
let path = Path::new("/home/user/docs/file.txt");
for component in path.components() {
println!("{:?}", component);
}
// 输出: RootDir, Normal("home"), Normal("user"), Normal("docs"), Normal("file.txt")
// 遍历祖先路径
for ancestor in path.ancestors() {
println!("{}", ancestor.display());
}
}
目录操作
use std::fs::{self, DirEntry};
use std::path::Path;
fn main() -> std::io::Result<()> {
// ============ 创建目录 ============
// 创建单级目录
fs::create_dir("new_dir")?;
// 递归创建多级目录
fs::create_dir_all("path/to/nested/dir")?;
// ============ 删除目录 ============
// 删除空目录
fs::remove_dir("empty_dir")?;
// 递归删除目录及内容
fs::remove_dir_all("dir_with_contents")?;
// ============ 读取目录内容 ============
// 方式1: 简单遍历
for entry in fs::read_dir(".")? {
let entry = entry?;
let path = entry.path();
let file_type = entry.file_type()?;
if file_type.is_dir() {
println!("[DIR] {}", path.display());
} else if file_type.is_file() {
println!("[FILE] {}", path.display());
} else if file_type.is_symlink() {
println!("[LINK] {}", path.display());
}
}
// 方式2: 递归遍历目录
fn visit_dirs(dir: &Path, depth: usize) -> std::io::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
let indent = " ".repeat(depth);
println!("{}{}", indent, path.file_name().unwrap().to_string_lossy());
if path.is_dir() {
visit_dirs(&path, depth + 1)?;
}
}
}
Ok(())
}
visit_dirs(Path::new("."), 0)?;
// ============ 获取特殊目录 ============
// 当前工作目录
let cwd = std::env::current_dir()?;
println!("当前目录: {}", cwd.display());
// 切换工作目录
std::env::set_current_dir("/tmp")?;
// 临时目录
let temp_dir = std::env::temp_dir();
println!("临时目录: {}", temp_dir.display());
Ok(())
}
文件元数据
use std::fs::{self, Metadata, Permissions};
use std::os::unix::fs::PermissionsExt; // Unix 特定
use std::time::SystemTime;
fn main() -> std::io::Result<()> {
let metadata = fs::metadata("file.txt")?;
// ============ 基本信息 ============
println!("是文件: {}", metadata.is_file());
println!("是目录: {}", metadata.is_dir());
println!("是符号链接: {}", metadata.is_symlink());
println!("文件大小: {} bytes", metadata.len());
// ============ 时间戳 ============
if let Ok(modified) = metadata.modified() {
println!("修改时间: {:?}", modified);
}
if let Ok(accessed) = metadata.accessed() {
println!("访问时间: {:?}", accessed);
}
if let Ok(created) = metadata.created() {
println!("创建时间: {:?}", created);
}
// ============ 权限操作 (Unix) ============
#[cfg(unix)]
{
let permissions = metadata.permissions();
let mode = permissions.mode();
println!("权限: {:o}", mode); // 八进制显示
println!("只读: {}", permissions.readonly());
// 设置权限
let mut perms = fs::metadata("file.txt")?.permissions();
perms.set_mode(0o644); // rw-r--r--
fs::set_permissions("file.txt", perms)?;
// 设置只读
let mut perms = fs::metadata("file.txt")?.permissions();
perms.set_readonly(true);
fs::set_permissions("file.txt", perms)?;
}
// ============ 符号链接元数据 ============
// metadata() 跟随符号链接
// symlink_metadata() 获取链接本身的信息
let link_metadata = fs::symlink_metadata("symlink")?;
Ok(())
}
文件系统操作
use std::fs;
use std::path::Path;
fn main() -> std::io::Result<()> {
// ============ 复制文件 ============
let bytes_copied = fs::copy("source.txt", "dest.txt")?;
println!("复制了 {} 字节", bytes_copied);
// ============ 重命名/移动文件 ============
fs::rename("old_name.txt", "new_name.txt")?;
fs::rename("file.txt", "subdir/file.txt")?; // 移动
// ============ 删除文件 ============
fs::remove_file("to_delete.txt")?;
// ============ 创建硬链接 ============
fs::hard_link("original.txt", "hardlink.txt")?;
// ============ 创建符号链接 ============
#[cfg(unix)]
std::os::unix::fs::symlink("target.txt", "symlink.txt")?;
#[cfg(windows)]
std::os::windows::fs::symlink_file("target.txt", "symlink.txt")?;
// ============ 读取符号链接目标 ============
let target = fs::read_link("symlink.txt")?;
println!("链接指向: {}", target.display());
Ok(())
}
异步文件操作 (Tokio)
[dependencies]
tokio = { version = "1", features = ["full"] }
use tokio::fs::{self, File};
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader, AsyncBufReadExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// ============ 快捷方法 ============
// 读取文件
let content = fs::read_to_string("config.txt").await?;
let bytes = fs::read("data.bin").await?;
// 写入文件
fs::write("output.txt", "Hello Async!").await?;
// ============ File 操作 ============
// 打开文件
let mut file = File::open("input.txt").await?;
let mut content = String::new();
file.read_to_string(&mut content).await?;
// 创建文件
let mut file = File::create("output.txt").await?;
file.write_all(b"Hello!").await?;
file.sync_all().await?;
// ============ 按行读取 ============
let file = File::open("log.txt").await?;
let reader = BufReader::new(file);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await? {
println!("{}", line);
}
// ============ 目录操作 ============
fs::create_dir_all("path/to/dir").await?;
fs::remove_dir_all("old_dir").await?;
let mut entries = fs::read_dir(".").await?;
while let Some(entry) = entries.next_entry().await? {
println!("{}", entry.path().display());
}
Ok(())
}
实用工具库
walkdir - 递归目录遍历
[dependencies]
walkdir = "2"
use walkdir::WalkDir;
fn main() {
// 递归遍历
for entry in WalkDir::new(".").into_iter().filter_map(|e| e.ok()) {
println!("{}", entry.path().display());
}
// 限制深度 + 过滤
for entry in WalkDir::new(".")
.max_depth(3)
.into_iter()
.filter_entry(|e| !e.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false))
.filter_map(|e| e.ok())
{
if entry.file_type().is_file() {
println!("{}", entry.path().display());
}
}
}
glob - 文件模式匹配
[dependencies]
glob = "0.3"
use glob::glob;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 匹配所有 .rs 文件
for entry in glob("src/**/*.rs")? {
println!("{}", entry?.display());
}
// 匹配多种模式
for entry in glob("*.{txt,md,toml}")? {
println!("{}", entry?.display());
}
Ok(())
}
tempfile - 临时文件
[dependencies]
tempfile = "3"
use tempfile::{tempfile, tempdir, NamedTempFile};
use std::io::Write;
fn main() -> std::io::Result<()> {
// 匿名临时文件(自动删除)
let mut file = tempfile()?;
writeln!(file, "temporary data")?;
// 命名临时文件
let mut named_file = NamedTempFile::new()?;
writeln!(named_file, "data")?;
println!("临时文件路径: {}", named_file.path().display());
// 离开作用域自动删除
// 保留临时文件
let (file, path) = NamedTempFile::new()?.keep()?;
// 临时目录
let temp_dir = tempdir()?;
let file_path = temp_dir.path().join("temp.txt");
std::fs::write(&file_path, "data")?;
// temp_dir 离开作用域时自动删除
Ok(())
}
错误处理
use std::fs::File;
use std::io::{self, Read, ErrorKind};
use std::path::Path;
// ============ 详细错误处理 ============
fn read_file_verbose(path: &str) -> io::Result<String> {
let mut file = match File::open(path) {
Ok(f) => f,
Err(e) => match e.kind() {
ErrorKind::NotFound => {
eprintln!("文件不存在: {}", path);
return Err(e);
}
ErrorKind::PermissionDenied => {
eprintln!("权限不足: {}", path);
return Err(e);
}
_ => {
eprintln!("打开文件失败: {}", e);
return Err(e);
}
}
};
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// ============ 使用 anyhow 简化 ============
use anyhow::{Context, Result};
fn read_config(path: &Path) -> Result<String> {
std::fs::read_to_string(path)
.with_context(|| format!("无法读取配置文件: {}", path.display()))
}
// ============ 检查文件存在后操作 ============
fn safe_read(path: &Path) -> io::Result<String> {
if !path.exists() {
return Err(io::Error::new(ErrorKind::NotFound, "文件不存在"));
}
if !path.is_file() {
return Err(io::Error::new(ErrorKind::InvalidInput, "不是文件"));
}
std::fs::read_to_string(path)
}
数据库编程
核心生态架构
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 数据库生态 │
├─────────────────────────────────────────────────────────────────────┤
│ ORM 框架 │ Diesel (同步) │ SeaORM (异步) │ rbatis │
├─────────────────────────────────────────────────────────────────────┤
│ SQL 工具库 │ SQLx (异步/编译时检查) │ rusqlite (SQLite) │
├─────────────────────────────────────────────────────────────────────┤
│ NoSQL │ mongodb │ redis │ sled (嵌入式) │
├─────────────────────────────────────────────────────────────────────┤
│ 连接池 │ deadpool │ r2d2 │ bb8 │
├─────────────────────────────────────────────────────────────────────┤
│ 支持数据库 │ PostgreSQL │ MySQL │ SQLite │ MSSQL │ MongoDB │
└─────────────────────────────────────────────────────────────────────┘
库选择指南
| 库 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| SQLx | SQL工具 | 编译时检查、异步、轻量 | 需要原生SQL控制 |
| Diesel | ORM | 类型安全、同步、成熟 | 传统同步应用 |
| SeaORM | ORM | 异步、动态查询、ActiveRecord | 异步Web服务 |
| rusqlite | SQLite | 同步、嵌入式、简单 | 本地应用/测试 |
SQLx - 异步 SQL 工具库
项目配置
[dependencies]
sqlx = { version = "0.7", features = [
"runtime-tokio", # 异步运行时
"tls-rustls", # TLS支持
"postgres", # PostgreSQL
# "mysql", # MySQL
# "sqlite", # SQLite
"macros", # 宏支持
"chrono", # 时间类型
"uuid", # UUID类型
] }
tokio = { version = "1", features = ["full"] }
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4", "serde"] }
serde = { version = "1", features = ["derive"] }
连接与连接池
use sqlx::{PgPool, Pool, Postgres};
use sqlx::postgres::PgPoolOptions;
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
// ============ 方式1: 快速连接 ============
let pool = PgPool::connect("postgres://user:pass@localhost/dbname").await?;
// ============ 方式2: 详细配置连接池 ============
let pool = PgPoolOptions::new()
.max_connections(10) // 最大连接数
.min_connections(2) // 最小连接数
.acquire_timeout(std::time::Duration::from_secs(30))
.idle_timeout(std::time::Duration::from_secs(600))
.max_lifetime(std::time::Duration::from_secs(1800))
.connect("postgres://user:pass@localhost/dbname")
.await?;
// ============ 方式3: 从环境变量 ============
// DATABASE_URL=postgres://user:pass@localhost/dbname
let pool = PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
// 检查连接
sqlx::query("SELECT 1").execute(&pool).await?;
println!("数据库连接成功!");
Ok(())
}
基本 CRUD 操作
use sqlx::{PgPool, Row, FromRow};
use chrono::{DateTime, Utc};
use uuid::Uuid;
// 定义模型
#[derive(Debug, FromRow)]
struct User {
id: i32,
username: String,
email: String,
created_at: DateTime<Utc>,
}
#[derive(Debug)]
struct CreateUser {
username: String,
email: String,
}
async fn crud_examples(pool: &PgPool) -> Result<(), sqlx::Error> {
// ============ CREATE ============
// 方式1: execute 返回影响行数
let result = sqlx::query(
"INSERT INTO users (username, email) VALUES ($1, $2)"
)
.bind("alice")
.bind("alice@example.com")
.execute(pool)
.await?;
println!("插入 {} 行", result.rows_affected());
// 方式2: 返回插入的ID
let id: (i32,) = sqlx::query_as(
"INSERT INTO users (username, email) VALUES ($1, $2) RETURNING id"
)
.bind("bob")
.bind("bob@example.com")
.fetch_one(pool)
.await?;
println!("新用户ID: {}", id.0);
// ============ READ ============
// 查询单条记录
let user: User = sqlx::query_as("SELECT * FROM users WHERE id = $1")
.bind(1)
.fetch_one(pool)
.await?;
println!("用户: {:?}", user);
// 查询可能不存在的记录
let user: Option<User> = sqlx::query_as("SELECT * FROM users WHERE id = $1")
.bind(999)
.fetch_optional(pool)
.await?;
// 查询多条记录
let users: Vec<User> = sqlx::query_as("SELECT * FROM users LIMIT $1")
.bind(10i64)
.fetch_all(pool)
.await?;
// 流式查询(大数据集)
use futures::TryStreamExt;
let mut stream = sqlx::query_as::<_, User>("SELECT * FROM users")
.fetch(pool);
while let Some(user) = stream.try_next().await? {
println!("流式: {:?}", user);
}
// ============ UPDATE ============
let result = sqlx::query(
"UPDATE users SET email = $1 WHERE id = $2"
)
.bind("newemail@example.com")
.bind(1)
.execute(pool)
.await?;
println!("更新 {} 行", result.rows_affected());
// ============ DELETE ============
let result = sqlx::query("DELETE FROM users WHERE id = $1")
.bind(1)
.execute(pool)
.await?;
println!("删除 {} 行", result.rows_affected());
Ok(())
}
编译时检查宏
use sqlx::{PgPool, FromRow};
#[derive(Debug, FromRow)]
struct User {
id: i32,
username: String,
email: String,
}
// 需要设置 DATABASE_URL 环境变量,编译时检查SQL
async fn compile_time_checked(pool: &PgPool) -> Result<(), sqlx::Error> {
// ============ query! 宏 - 返回匿名类型 ============
let row = sqlx::query!(
"SELECT id, username, email FROM users WHERE id = $1",
1i32 // 参数类型会被检查
)
.fetch_one(pool)
.await?;
// 字段名在编译时已知
println!("ID: {}, Username: {}", row.id, row.username);
// ============ query_as! 宏 - 映射到结构体 ============
let user = sqlx::query_as!(
User,
"SELECT id, username, email FROM users WHERE id = $1",
1i32
)
.fetch_one(pool)
.await?;
// ============ 可空字段处理 ============
let row = sqlx::query!(
r#"SELECT id, username, bio as "bio?" FROM users WHERE id = $1"#,
1i32
)
.fetch_one(pool)
.await?;
// row.bio 类型为 Option<String>
// ============ 强制非空 ============
let row = sqlx::query!(
r#"SELECT id, username as "username!" FROM users WHERE id = $1"#,
1i32
)
.fetch_one(pool)
.await?;
// ============ 类型转换 ============
let row = sqlx::query!(
r#"SELECT COUNT(*) as "count!: i64" FROM users"#
)
.fetch_one(pool)
.await?;
println!("总数: {}", row.count);
Ok(())
}
// 离线模式(CI/CD用)
// 1. cargo sqlx prepare 生成 .sqlx 目录
// 2. 设置 SQLX_OFFLINE=true
事务处理
use sqlx::PgPool;
async fn transfer_money(
pool: &PgPool,
from_id: i32,
to_id: i32,
amount: f64,
) -> Result<(), sqlx::Error> {
// ============ 方式1: 简单事务 ============
let mut tx = pool.begin().await?;
// 扣款
sqlx::query("UPDATE accounts SET balance = balance - $1 WHERE id = $2")
.bind(amount)
.bind(from_id)
.execute(&mut *tx)
.await?;
// 入账
sqlx::query("UPDATE accounts SET balance = balance + $1 WHERE id = $2")
.bind(amount)
.bind(to_id)
.execute(&mut *tx)
.await?;
// 提交(如果函数提前返回Err,事务自动回滚)
tx.commit().await?;
Ok(())
}
// ============ 方式2: 嵌套事务(Savepoint)============
async fn nested_transaction(pool: &PgPool) -> Result<(), sqlx::Error> {
let mut tx = pool.begin().await?;
sqlx::query("INSERT INTO logs (msg) VALUES ('outer')")
.execute(&mut *tx)
.await?;
// 创建保存点
let mut savepoint = tx.begin().await?;
let result = sqlx::query("INSERT INTO users (name) VALUES ('test')")
.execute(&mut *savepoint)
.await;
match result {
Ok(_) => savepoint.commit().await?,
Err(_) => {
// 只回滚到保存点,外层事务继续
savepoint.rollback().await?;
}
}
tx.commit().await?;
Ok(())
}
Diesel - 类型安全 ORM
项目配置
[dependencies]
diesel = { version = "2", features = ["postgres", "r2d2", "chrono"] }
dotenvy = "0.15"
[dependencies.diesel_migrations]
version = "2"
初始化项目
# 安装CLI
cargo install diesel_cli --no-default-features --features postgres
# 初始化
diesel setup
# 创建迁移
diesel migration generate create_users
# 运行迁移
diesel migration run
# 回滚
diesel migration revert
迁移文件
-- migrations/2024xxx_create_users/up.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- migrations/2024xxx_create_users/down.sql
DROP TABLE users;
Schema 和模型
// src/schema.rs (diesel自动生成)
diesel::table! {
users (id) {
id -> Int4,
username -> Varchar,
email -> Varchar,
created_at -> Timestamp,
}
}
// src/models.rs
use diesel::prelude::*;
use chrono::NaiveDateTime;
use crate::schema::users;
// 查询用模型
#[derive(Debug, Queryable, Selectable)]
#[diesel(table_name = users)]
pub struct User {
pub id: i32,
pub username: String,
pub email: String,
pub created_at: NaiveDateTime,
}
// 插入用模型
#[derive(Debug, Insertable)]
#[diesel(table_name = users)]
pub struct NewUser<'a> {
pub username: &'a str,
pub email: &'a str,
}
// 更新用模型
#[derive(Debug, AsChangeset)]
#[diesel(table_name = users)]
pub struct UpdateUser<'a> {
pub username: Option<&'a str>,
pub email: Option<&'a str>,
}
CRUD 操作
use diesel::prelude::*;
use diesel::pg::PgConnection;
use diesel::r2d2::{self, ConnectionManager};
type DbPool = r2d2::Pool<ConnectionManager<PgConnection>>;
// 建立连接池
fn establish_pool() -> DbPool {
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let manager = ConnectionManager::<PgConnection>::new(database_url);
r2d2::Pool::builder()
.max_size(10)
.build(manager)
.expect("Failed to create pool")
}
// ============ CREATE ============
fn create_user(conn: &mut PgConnection, name: &str, mail: &str) -> QueryResult<User> {
use crate::schema::users;
let new_user = NewUser {
username: name,
email: mail,
};
diesel::insert_into(users::table)
.values(&new_user)
.returning(User::as_returning())
.get_result(conn)
}
// 批量插入
fn create_users(conn: &mut PgConnection, new_users: &[NewUser]) -> QueryResult<Vec<User>> {
use crate::schema::users;
diesel::insert_into(users::table)
.values(new_users)
.returning(User::as_returning())
.get_results(conn)
}
// ============ READ ============
fn get_user(conn: &mut PgConnection, user_id: i32) -> QueryResult<User> {
use crate::schema::users::dsl::*;
users.find(user_id).first(conn)
}
fn get_user_by_name(conn: &mut PgConnection, name: &str) -> QueryResult<Option<User>> {
use crate::schema::users::dsl::*;
users
.filter(username.eq(name))
.first(conn)
.optional()
}
fn list_users(conn: &mut PgConnection, page: i64, per_page: i64) -> QueryResult<Vec<User>> {
use crate::schema::users::dsl::*;
users
.order(created_at.desc())
.limit(per_page)
.offset(page * per_page)
.load(conn)
}
// 复杂查询
fn search_users(conn: &mut PgConnection, query: &str) -> QueryResult<Vec<User>> {
use crate::schema::users::dsl::*;
users
.filter(username.ilike(format!("%{}%", query)))
.or_filter(email.ilike(format!("%{}%", query)))
.order(username.asc())
.load(conn)
}
// ============ UPDATE ============
fn update_user(conn: &mut PgConnection, user_id: i32, update: UpdateUser) -> QueryResult<User> {
use crate::schema::users::dsl::*;
diesel::update(users.find(user_id))
.set(&update)
.returning(User::as_returning())
.get_result(conn)
}
// ============ DELETE ============
fn delete_user(conn: &mut PgConnection, user_id: i32) -> QueryResult<usize> {
use crate::schema::users::dsl::*;
diesel::delete(users.find(user_id)).execute(conn)
}
事务
fn transfer_in_transaction(conn: &mut PgConnection) -> QueryResult<()> {
conn.transaction(|conn| {
// 所有操作在同一事务中
diesel::update(accounts.find(1))
.set(balance.eq(balance - 100))
.execute(conn)?;
diesel::update(accounts.find(2))
.set(balance.eq(balance + 100))
.execute(conn)?;
Ok(())
})
}
SeaORM - 异步 ORM
项目配置
[dependencies]
sea-orm = { version = "0.12", features = [
"runtime-tokio-rustls",
"sqlx-postgres",
"macros",
] }
tokio = { version = "1", features = ["full"] }
Entity 定义
// entity/user.rs
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub username: String,
pub email: String,
pub created_at: DateTimeUtc,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::post::Entity")]
Posts,
}
impl Related<super::post::Entity> for Entity {
fn to() -> RelationDef {
Relation::Posts.def()
}
}
impl ActiveModelBehavior for ActiveModel {}
CRUD 操作
use sea_orm::*;
// ============ 连接 ============
async fn connect() -> Result<DatabaseConnection, DbErr> {
let db = Database::connect("postgres://user:pass@localhost/db").await?;
Ok(db)
}
// ============ CREATE ============
async fn create_user(db: &DatabaseConnection) -> Result<user::Model, DbErr> {
let new_user = user::ActiveModel {
username: Set("alice".to_owned()),
email: Set("alice@example.com".to_owned()),
..Default::default()
};
new_user.insert(db).await
}
// ============ READ ============
async fn find_user(db: &DatabaseConnection, id: i32) -> Result<Option<user::Model>, DbErr> {
User::find_by_id(id).one(db).await
}
async fn find_users(db: &DatabaseConnection) -> Result<Vec<user::Model>, DbErr> {
User::find()
.filter(user::Column::Username.contains("a"))
.order_by_asc(user::Column::Id)
.limit(10)
.all(db)
.await
}
// ============ UPDATE ============
async fn update_user(db: &DatabaseConnection, id: i32) -> Result<user::Model, DbErr> {
let user: user::ActiveModel = User::find_by_id(id)
.one(db)
.await?
.ok_or(DbErr::RecordNotFound("User not found".into()))?
.into();
let mut user = user;
user.email = Set("new@example.com".to_owned());
user.update(db).await
}
// ============ DELETE ============
async fn delete_user(db: &DatabaseConnection, id: i32) -> Result<DeleteResult, DbErr> {
User::delete_by_id(id).exec(db).await
}
// ============ 事务 ============
async fn transaction_example(db: &DatabaseConnection) -> Result<(), DbErr> {
db.transaction::<_, (), DbErr>(|txn| {
Box::pin(async move {
// 事务内操作
let user = user::ActiveModel {
username: Set("bob".to_owned()),
email: Set("bob@example.com".to_owned()),
..Default::default()
};
user.insert(txn).await?;
Ok(())
})
})
.await
}
SQLite
[dependencies]
rusqlite = { version = "0.31", features = ["bundled"] }
use rusqlite::{Connection, Result, params};
#[derive(Debug)]
struct User {
id: i32,
name: String,
email: String,
}
fn main() -> Result<()> {
// 连接(内存数据库或文件)
let conn = Connection::open("app.db")?;
// let conn = Connection::open_in_memory()?;
// 创建表
conn.execute(
"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
)",
[],
)?;
// ============ CREATE ============
conn.execute(
"INSERT INTO users (name, email) VALUES (?1, ?2)",
params!["Alice", "alice@example.com"],
)?;
let last_id = conn.last_insert_rowid();
// ============ READ ============
let mut stmt = conn.prepare("SELECT id, name, email FROM users WHERE id = ?1")?;
let user = stmt.query_row([1], |row| {
Ok(User {
id: row.get(0)?,
name: row.get(1)?,
email: row.get(2)?,
})
})?;
println!("{:?}", user);
// 查询多条
let mut stmt = conn.prepare("SELECT id, name, email FROM users")?;
let users = stmt.query_map([], |row| {
Ok(User {
id: row.get(0)?,
name: row.get(1)?,
email: row.get(2)?,
})
})?;
for user in users {
println!("{:?}", user?);
}
// ============ 事务 ============
let tx = conn.transaction()?;
tx.execute("INSERT INTO users (name, email) VALUES (?1, ?2)",
params!["Bob", "bob@example.com"])?;
tx.commit()?;
Ok(())
}
Redis
[dependencies]
redis = { version = "0.25", features = ["tokio-comp", "connection-manager"] }
tokio = { version = "1", features = ["full"] }
use redis::AsyncCommands;
#[tokio::main]
async fn main() -> redis::RedisResult<()> {
// 连接
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_multiplexed_async_connection().await?;
// ============ 字符串 ============
con.set("key", "value").await?;
let val: String = con.get("key").await?;
// 带过期时间
con.set_ex("temp_key", "temp_value", 60).await?; // 60秒
// ============ 哈希 ============
con.hset("user:1", "name", "Alice").await?;
con.hset("user:1", "email", "alice@example.com").await?;
let name: String = con.hget("user:1", "name").await?;
let all: std::collections::HashMap<String, String> = con.hgetall("user:1").await?;
// ============ 列表 ============
con.rpush("queue", "item1").await?;
con.rpush("queue", "item2").await?;
let item: String = con.lpop("queue", None).await?;
// ============ 集合 ============
con.sadd("tags", "rust").await?;
con.sadd("tags", "database").await?;
let members: Vec<String> = con.smembers("tags").await?;
// ============ 有序集合 ============
con.zadd("leaderboard", "player1", 100).await?;
con.zadd("leaderboard", "player2", 200).await?;
let top: Vec<(String, i32)> = con.zrevrange_withscores("leaderboard", 0, 9).await?;
// ============ 管道 ============
let (k1, k2): (String, String) = redis::pipe()
.get("key1")
.get("key2")
.query_async(&mut con)
.await?;
Ok(())
}
MongoDB
[dependencies]
mongodb = "2"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
futures = "0.3"
use mongodb::{Client, Collection, bson::doc};
use serde::{Deserialize, Serialize};
use futures::TryStreamExt;
#[derive(Debug, Serialize, Deserialize)]
struct User {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
id: Option<mongodb::bson::oid::ObjectId>,
username: String,
email: String,
age: i32,
}
#[tokio::main]
async fn main() -> mongodb::error::Result<()> {
// 连接
let client = Client::with_uri_str("mongodb://localhost:27017").await?;
let db = client.database("mydb");
let collection: Collection<User> = db.collection("users");
// ============ CREATE ============
let user = User {
id: None,
username: "alice".to_string(),
email: "alice@example.com".to_string(),
age: 25,
};
let result = collection.insert_one(user, None).await?;
println!("Inserted ID: {:?}", result.inserted_id);
// 批量插入
let users = vec![
User { id: None, username: "bob".into(), email: "bob@example.com".into(), age: 30 },
User { id: None, username: "carol".into(), email: "carol@example.com".into(), age: 28 },
];
collection.insert_many(users, None).await?;
// ============ READ ============
// 查询单个
let user = collection.find_one(doc! { "username": "alice" }, None).await?;
println!("{:?}", user);
// 查询多个
let mut cursor = collection.find(doc! { "age": { "$gte": 25 } }, None).await?;
while let Some(user) = cursor.try_next().await? {
println!("{:?}", user);
}
// ============ UPDATE ============
collection.update_one(
doc! { "username": "alice" },
doc! { "$set": { "age": 26 } },
None,
).await?;
// ============ DELETE ============
collection.delete_one(doc! { "username": "bob" }, None).await?;
// ============ 聚合 ============
let pipeline = vec![
doc! { "$match": { "age": { "$gte": 20 } } },
doc! { "$group": { "_id": null, "avg_age": { "$avg": "$age" } } },
];
let mut cursor = collection.aggregate(pipeline, None).await?;
while let Some(doc) = cursor.try_next().await? {
println!("{:?}", doc);
}
Ok(())
}
数据库迁移管理
SQLx 迁移
# 创建迁移
sqlx migrate add create_users
# 运行迁移
sqlx migrate run
# 回滚
sqlx migrate revert
// 代码中嵌入迁移
use sqlx::migrate::Migrator;
static MIGRATOR: Migrator = sqlx::migrate!("./migrations");
async fn run_migrations(pool: &PgPool) -> Result<(), sqlx::Error> {
MIGRATOR.run(pool).await?;
Ok(())
}
Diesel 迁移
diesel migration generate create_users
diesel migration run
diesel migration revert
diesel migration redo
总结对比
| 特性 | SQLx | Diesel | SeaORM |
|---|---|---|---|
| 异步支持 | ✅ | ❌ | ✅ |
| 编译时检查 | ✅ 宏 | ✅ 类型系统 | ❌ |
| 学习曲线 | 低 | 中 | 中 |
| 原生SQL | ✅ | 部分 | 部分 |
| ORM功能 | ❌ | ✅ | ✅ |
| 动态查询 | ✅ | 中等 | ✅ |
| 迁移工具 | ✅ | ✅ | ✅ |
Unsafe 编程
核心概念
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 安全模型 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Safe Rust │ 编译器保证内存安全、无数据竞争 │
│ ───────────────────────────────────────────────────────────── │
│ Unsafe Rust │ 程序员承诺代码安全,编译器信任 │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Unsafe 五大超能力 │
├─────────────────────────────────────────────────────────────────────┤
│ 1. 解引用裸指针 *const T / *mut T │
│ 2. 调用 unsafe 函数 unsafe fn / extern "C" fn │
│ 3. 访问可变静态变量 static mut │
│ 4. 实现 unsafe trait Send / Sync 等 │
│ 5. 访问 union 字段 union 类型 │
└─────────────────────────────────────────────────────────────────────┘
unsafe 不意味着什么
// unsafe 不会关闭借用检查器!
unsafe {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // ❌ 仍然报错!
}
// unsafe 不会关闭类型检查!
unsafe {
let x: i32 = "hello"; // ❌ 仍然报错!
}
裸指针 (Raw Pointers)
基本操作
fn main() {
// ============ 创建裸指针(安全的)============
let x = 42;
let y = &x as *const i32; // 不可变裸指针
let z = &x as *const i32 as *mut i32; // 可变裸指针(危险)
let mut value = 10;
let ptr_mut = &mut value as *mut i32;
// 从引用创建
let reference = &value;
let ptr = reference as *const i32;
// ============ 解引用裸指针(需要 unsafe)============
unsafe {
println!("y = {}", *y);
*ptr_mut = 20;
println!("value = {}", *ptr_mut);
}
// ============ 裸指针可以是空的 ============
let null_ptr: *const i32 = std::ptr::null();
let null_mut: *mut i32 = std::ptr::null_mut();
// 检查是否为空
if !null_ptr.is_null() {
unsafe { println!("{}", *null_ptr); }
}
// ============ 裸指针可以指向任意地址 ============
let addr = 0x012345usize;
let _ptr = addr as *const i32; // 创建是安全的,解引用是危险的
}
指针算术
fn main() {
let arr = [1, 2, 3, 4, 5];
let ptr = arr.as_ptr();
unsafe {
// 指针偏移
println!("arr[0] = {}", *ptr);
println!("arr[2] = {}", *ptr.add(2)); // 向后移动2个元素
println!("arr[4] = {}", *ptr.offset(4)); // 同上,但参数是 isize
// 遍历数组
for i in 0..arr.len() {
println!("{}", *ptr.add(i));
}
}
// ============ 可变指针操作 ============
let mut arr = [1, 2, 3, 4, 5];
let ptr = arr.as_mut_ptr();
unsafe {
*ptr.add(2) = 100; // arr[2] = 100
// 写入值
ptr.add(3).write(200);
// 读取值(不移动所有权)
let val = ptr.add(1).read();
println!("val = {}", val);
}
println!("{:?}", arr); // [1, 2, 100, 200, 5]
}
指针与切片转换
use std::slice;
fn main() {
// ============ 从裸指针创建切片 ============
let arr = [1, 2, 3, 4, 5];
let ptr = arr.as_ptr();
unsafe {
let slice = slice::from_raw_parts(ptr, 3);
println!("{:?}", slice); // [1, 2, 3]
}
// ============ 可变切片 ============
let mut arr = [1, 2, 3, 4, 5];
let ptr = arr.as_mut_ptr();
unsafe {
let slice = slice::from_raw_parts_mut(ptr, arr.len());
slice[0] = 100;
}
println!("{:?}", arr); // [100, 2, 3, 4, 5]
// ============ 实现 split_at_mut(标准库实现原理)============
fn split_at_mut<T>(slice: &mut [T], mid: usize) -> (&mut [T], &mut [T]) {
let len = slice.len();
let ptr = slice.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
let mut arr = [1, 2, 3, 4, 5];
let (left, right) = split_at_mut(&mut arr, 2);
println!("{:?}, {:?}", left, right); // [1, 2], [3, 4, 5]
}
Unsafe 函数与方法
定义和调用
// ============ 定义 unsafe 函数 ============
unsafe fn dangerous() {
println!("危险操作!");
}
// unsafe 函数体内部被视为 unsafe 块
unsafe fn process_raw_pointer(ptr: *const i32) -> i32 {
*ptr // 可以直接解引用,无需再包装 unsafe
}
fn main() {
// 调用 unsafe 函数需要 unsafe 块
unsafe {
dangerous();
let x = 42;
let val = process_raw_pointer(&x);
println!("val = {}", val);
}
}
// ============ 安全抽象包装 unsafe ============
/// 安全地获取向量指定位置的元素
///
/// # Panics
/// 如果索引越界
fn safe_get(v: &Vec<i32>, index: usize) -> i32 {
assert!(index < v.len(), "索引越界");
unsafe {
*v.as_ptr().add(index)
}
}
unsafe trait
// ============ 定义 unsafe trait ============
unsafe trait UnsafeTrait {
fn do_something(&self);
}
// 实现 unsafe trait 需要 unsafe impl
unsafe impl UnsafeTrait for i32 {
fn do_something(&self) {
println!("i32: {}", self);
}
}
// ============ Send 和 Sync(标准库中的 unsafe trait)============
// Send: 类型可以安全地在线程间转移所有权
// Sync: 类型可以安全地在线程间共享引用
// 裸指针默认不是 Send/Sync
struct MyBox(*mut i32);
// 程序员保证线程安全后可以手动实现
unsafe impl Send for MyBox {}
unsafe impl Sync for MyBox {}
// ============ 自动实现规则 ============
// 如果类型的所有字段都是 Send,则类型自动 Send
// 如果类型的所有字段都是 Sync,则类型自动 Sync
// 使用 PhantomData 可以影响自动实现
use std::marker::PhantomData;
struct NotSend {
_marker: PhantomData<*const ()>, // *const () 不是 Send
}
// NotSend 不会自动实现 Send
静态变量与内部可变性
可变静态变量
// ============ 不可变静态变量(安全)============
static GREETING: &str = "Hello, World!";
static COUNT: i32 = 42;
// ============ 可变静态变量(unsafe)============
static mut COUNTER: i32 = 0;
fn increment() {
unsafe {
COUNTER += 1;
}
}
fn get_count() -> i32 {
unsafe { COUNTER }
}
fn main() {
println!("{}", GREETING);
increment();
increment();
println!("Counter: {}", get_count()); // 2
}
// ============ 更安全的替代方案 ============
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::OnceLock;
// 原子类型(推荐)
static ATOMIC_COUNTER: AtomicI32 = AtomicI32::new(0);
fn safe_increment() {
ATOMIC_COUNTER.fetch_add(1, Ordering::SeqCst);
}
// OnceLock 用于延迟初始化
static CONFIG: OnceLock<String> = OnceLock::new();
fn get_config() -> &'static String {
CONFIG.get_or_init(|| {
// 复杂的初始化逻辑
String::from("default_config")
})
}
外部静态变量
extern "C" {
static errno: i32; // 外部不可变
static mut global: i32; // 外部可变
}
fn check_errno() {
unsafe {
if errno != 0 {
println!("Error: {}", errno);
}
}
}
Union 类型
// ============ 定义 union ============
#[repr(C)]
union IntOrFloat {
i: i32,
f: f32,
}
fn main() {
let mut u = IntOrFloat { i: 42 };
// 读取 union 字段需要 unsafe
unsafe {
println!("As int: {}", u.i);
println!("As float: {}", u.f); // 重新解释位模式
}
// 写入不需要 unsafe
u.f = 3.14;
unsafe {
println!("As float: {}", u.f);
println!("As int: {}", u.i); // 查看 3.14 的位表示
}
}
// ============ 实际用途:类型双关(Type Punning)============
union FloatBits {
f: f32,
bits: u32,
}
fn float_to_bits(f: f32) -> u32 {
let fb = FloatBits { f };
unsafe { fb.bits }
}
fn bits_to_float(bits: u32) -> f32 {
let fb = FloatBits { bits };
unsafe { fb.f }
}
// ============ 带 ManuallyDrop 的 union(存储非 Copy 类型)============
use std::mem::ManuallyDrop;
union MaybeString {
none: (),
some: ManuallyDrop<String>,
}
impl MaybeString {
fn new_some(s: String) -> Self {
MaybeString {
some: ManuallyDrop::new(s),
}
}
fn new_none() -> Self {
MaybeString { none: () }
}
unsafe fn take(&mut self) -> String {
ManuallyDrop::take(&mut self.some)
}
}
FFI(外部函数接口)
调用 C 函数
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void};
// ============ 声明外部函数 ============
extern "C" {
fn abs(input: c_int) -> c_int;
fn strlen(s: *const c_char) -> usize;
fn printf(format: *const c_char, ...) -> c_int;
// 系统调用
fn malloc(size: usize) -> *mut c_void;
fn free(ptr: *mut c_void);
}
fn main() {
unsafe {
// 调用 abs
println!("abs(-5) = {}", abs(-5));
// 调用 strlen
let s = CString::new("Hello").unwrap();
println!("strlen = {}", strlen(s.as_ptr()));
// 调用 printf
let fmt = CString::new("Number: %d\n").unwrap();
printf(fmt.as_ptr(), 42 as c_int);
}
}
// ============ 字符串转换 ============
fn rust_to_c_string(s: &str) -> CString {
CString::new(s).expect("CString::new failed")
}
fn c_to_rust_string(ptr: *const c_char) -> String {
unsafe {
CStr::from_ptr(ptr)
.to_string_lossy()
.into_owned()
}
}
链接外部库
// Cargo.toml
// [build-dependencies]
// cc = "1"
// build.rs
fn main() {
// 链接系统库
println!("cargo:rustc-link-lib=m"); // libm
println!("cargo:rustc-link-lib=ssl"); // libssl
// 链接静态库
println!("cargo:rustc-link-lib=static=mylib");
println!("cargo:rustc-link-search=native=/path/to/lib");
}
// ============ 使用 cc crate 编译 C 代码 ============
// build.rs
fn main() {
cc::Build::new()
.file("src/native.c")
.compile("native");
}
// src/native.c
#include <stdint.h>
int32_t add(int32_t a, int32_t b) {
return a + b;
}
// src/main.rs
extern "C" {
fn add(a: i32, b: i32) -> i32;
}
fn main() {
unsafe {
println!("1 + 2 = {}", add(1, 2));
}
}
暴露 Rust 函数给 C
// ============ 导出函数 ============
#[no_mangle] // 防止名称修饰
pub extern "C" fn rust_add(a: i32, b: i32) -> i32 {
a + b
}
#[no_mangle]
pub extern "C" fn rust_hello() {
println!("Hello from Rust!");
}
// ============ 带回调的函数 ============
type Callback = extern "C" fn(i32) -> i32;
#[no_mangle]
pub extern "C" fn process_with_callback(x: i32, cb: Callback) -> i32 {
cb(x * 2)
}
// ============ 返回字符串 ============
use std::ffi::CString;
use std::os::raw::c_char;
#[no_mangle]
pub extern "C" fn get_greeting() -> *mut c_char {
let s = CString::new("Hello from Rust!").unwrap();
s.into_raw() // 转移所有权给 C
}
#[no_mangle]
pub extern "C" fn free_string(s: *mut c_char) {
if !s.is_null() {
unsafe {
drop(CString::from_raw(s)); // 收回所有权并释放
}
}
}
复杂数据结构传递
use std::os::raw::{c_char, c_int};
// ============ 结构体布局 ============
#[repr(C)] // 使用 C 的内存布局
pub struct Point {
x: f64,
y: f64,
}
#[repr(C)]
pub struct Person {
name: *const c_char,
age: c_int,
}
extern "C" {
fn process_point(p: Point) -> f64;
fn process_point_ptr(p: *const Point) -> f64;
}
// ============ 不透明类型(Opaque Type)============
// C 端无需知道内部结构
#[repr(C)]
pub struct OpaqueHandle {
_private: [u8; 0], // 零大小,阻止实例化
}
pub struct RealData {
data: Vec<u8>,
name: String,
}
#[no_mangle]
pub extern "C" fn create_handle() -> *mut OpaqueHandle {
let data = Box::new(RealData {
data: vec![1, 2, 3],
name: "test".to_string(),
});
Box::into_raw(data) as *mut OpaqueHandle
}
#[no_mangle]
pub extern "C" fn destroy_handle(handle: *mut OpaqueHandle) {
if !handle.is_null() {
unsafe {
drop(Box::from_raw(handle as *mut RealData));
}
}
}
内联汇编
use std::arch::asm;
fn main() {
// ============ 基本语法 ============
let x: u64;
unsafe {
asm!(
"mov {}, 42",
out(reg) x,
);
}
println!("x = {}", x); // 42
// ============ 输入输出 ============
let a: u64 = 10;
let b: u64 = 20;
let result: u64;
unsafe {
asm!(
"add {0}, {1}",
inout(reg) a => result,
in(reg) b,
);
}
println!("10 + 20 = {}", result);
// ============ 多条指令 ============
let value: u64;
unsafe {
asm!(
"mov {tmp}, {x}",
"add {tmp}, {y}",
"mov {out}, {tmp}",
x = in(reg) 5u64,
y = in(reg) 6u64,
tmp = out(reg) _,
out = out(reg) value,
);
}
println!("5 + 6 = {}", value);
// ============ 调用系统调用(Linux x86_64)============
#[cfg(target_os = "linux")]
{
let msg = "Hello from syscall!\n";
let len = msg.len();
let ptr = msg.as_ptr();
unsafe {
asm!(
"syscall",
in("rax") 1, // syscall number (write)
in("rdi") 1, // fd (stdout)
in("rsi") ptr, // buffer
in("rdx") len, // count
out("rcx") _,
out("r11") _,
options(nostack),
);
}
}
}
// ============ CPUID 示例 ============
#[cfg(target_arch = "x86_64")]
fn get_cpu_vendor() -> String {
let mut ebx: u32;
let mut ecx: u32;
let mut edx: u32;
unsafe {
asm!(
"cpuid",
inout("eax") 0 => _,
out("ebx") ebx,
out("ecx") ecx,
out("edx") edx,
);
}
let vendor = [ebx, edx, ecx];
let bytes: &[u8] = unsafe {
std::slice::from_raw_parts(vendor.as_ptr() as *const u8, 12)
};
String::from_utf8_lossy(bytes).to_string()
}
内存布局控制
repr 属性
// ============ #[repr(C)] - C 兼容布局 ============
#[repr(C)]
struct CStruct {
a: u8, // 偏移 0
b: u32, // 偏移 4(填充3字节)
c: u16, // 偏移 8
} // 总大小 12(填充2字节)
// ============ #[repr(packed)] - 紧凑布局 ============
#[repr(packed)]
struct PackedStruct {
a: u8, // 偏移 0
b: u32, // 偏移 1(无填充)
c: u16, // 偏移 5
} // 总大小 7
// ============ #[repr(align(N))] - 指定对齐 ============
#[repr(align(16))]
struct Aligned16 {
data: [u8; 4],
}
// ============ 组合使用 ============
#[repr(C, packed)]
struct CPackedStruct {
a: u8,
b: u32,
}
#[repr(C, align(8))]
struct CAligned8 {
a: u32,
}
// ============ #[repr(transparent)] - 与内部类型相同布局 ============
#[repr(transparent)]
struct Wrapper(u32); // 与 u32 完全相同的布局
fn main() {
use std::mem::{size_of, align_of};
println!("CStruct: size={}, align={}",
size_of::<CStruct>(), align_of::<CStruct>());
println!("PackedStruct: size={}, align={}",
size_of::<PackedStruct>(), align_of::<PackedStruct>());
println!("Aligned16: size={}, align={}",
size_of::<Aligned16>(), align_of::<Aligned16>());
}
手动内存管理
use std::alloc::{alloc, dealloc, realloc, Layout};
use std::ptr;
fn main() {
unsafe {
// ============ 分配内存 ============
let layout = Layout::new::<u32>();
let ptr = alloc(layout) as *mut u32;
if ptr.is_null() {
panic!("内存分配失败");
}
// 写入值
ptr.write(42);
println!("value = {}", ptr.read());
// 释放内存
dealloc(ptr as *mut u8, layout);
// ============ 分配数组 ============
let layout = Layout::array::<i32>(10).unwrap();
let arr = alloc(layout) as *mut i32;
// 初始化数组
for i in 0..10 {
arr.add(i).write(i as i32);
}
// 使用数组
for i in 0..10 {
print!("{} ", arr.add(i).read());
}
println!();
dealloc(arr as *mut u8, layout);
// ============ 重新分配 ============
let layout = Layout::array::<i32>(5).unwrap();
let ptr = alloc(layout) as *mut i32;
// 扩大数组
let new_layout = Layout::array::<i32>(10).unwrap();
let new_ptr = realloc(ptr as *mut u8, layout, new_layout.size()) as *mut i32;
dealloc(new_ptr as *mut u8, new_layout);
}
}
// ============ 使用 Box 进行底层操作 ============
fn box_operations() {
// 从裸指针创建 Box
let ptr = Box::into_raw(Box::new(42));
unsafe {
// 使用裸指针
println!("value = {}", *ptr);
// 转回 Box 以自动释放
let _ = Box::from_raw(ptr);
}
// 带自定义布局分配
let layout = Layout::new::<[u8; 1024]>();
let ptr = unsafe { alloc(layout) };
// ... 使用内存 ...
unsafe { dealloc(ptr, layout) };
}
常见 Unsafe 模式
自引用结构
use std::pin::Pin;
use std::marker::PhantomPinned;
struct SelfReferential {
data: String,
// 指向 data 的指针
ptr: *const String,
_pin: PhantomPinned,
}
impl SelfReferential {
fn new(data: String) -> Pin<Box<Self>> {
let mut boxed = Box::new(SelfReferential {
data,
ptr: std::ptr::null(),
_pin: PhantomPinned,
});
// 设置自引用
let ptr = &boxed.data as *const String;
boxed.ptr = ptr;
// Pin 住,防止移动
Box::into_pin(boxed)
}
fn get_data(self: Pin<&Self>) -> &str {
&self.data
}
fn get_ptr_data(self: Pin<&Self>) -> &str {
unsafe { &*self.ptr }
}
}
内部可变性
use std::cell::UnsafeCell;
pub struct MyCell<T> {
value: UnsafeCell<T>,
}
impl<T: Copy> MyCell<T> {
pub fn new(value: T) -> Self {
MyCell {
value: UnsafeCell::new(value),
}
}
pub fn get(&self) -> T {
unsafe { *self.value.get() }
}
pub fn set(&self, value: T) {
unsafe {
*self.value.get() = value;
}
}
}
// 单线程安全,因此不实现 Sync
// impl<T> !Sync for MyCell<T> {} // 自动的
侵入式链表
use std::ptr::NonNull;
struct Node<T> {
data: T,
next: Option<NonNull<Node<T>>>,
prev: Option<NonNull<Node<T>>>,
}
pub struct LinkedList<T> {
head: Option<NonNull<Node<T>>>,
tail: Option<NonNull<Node<T>>>,
len: usize,
}
impl<T> LinkedList<T> {
pub fn new() -> Self {
LinkedList {
head: None,
tail: None,
len: 0,
}
}
pub fn push_back(&mut self, data: T) {
let node = Box::new(Node {
data,
next: None,
prev: self.tail,
});
let node_ptr = NonNull::new(Box::into_raw(node));
match self.tail {
None => {
self.head = node_ptr;
}
Some(mut tail) => unsafe {
tail.as_mut().next = node_ptr;
}
}
self.tail = node_ptr;
self.len += 1;
}
pub fn pop_front(&mut self) -> Option<T> {
self.head.map(|node| unsafe {
let node = Box::from_raw(node.as_ptr());
self.head = node.next;
match self.head {
None => self.tail = None,
Some(mut head) => head.as_mut().prev = None,
}
self.len -= 1;
node.data
})
}
}
impl<T> Drop for LinkedList<T> {
fn drop(&mut self) {
while self.pop_front().is_some() {}
}
}
Unsafe 实践
最小化 unsafe 范围
// ❌ 不好:整个函数是 unsafe
unsafe fn bad_approach(data: &[u8]) -> u32 {
let ptr = data.as_ptr() as *const u32;
// ... 大量安全代码 ...
*ptr
}
// ✅ 好:只在必要处使用 unsafe
fn good_approach(data: &[u8]) -> u32 {
assert!(data.len() >= 4, "数据太短");
// ... 安全代码 ...
// 只有这一行需要 unsafe
unsafe {
let ptr = data.as_ptr() as *const u32;
ptr.read_unaligned()
}
}
文档化安全约束
/// 从字节切片读取 u32
///
/// # Safety
///
/// 调用者必须确保:
/// - `data` 至少有 4 个字节
/// - `data` 的起始地址对 u32 对齐(或使用 read_unaligned)
pub unsafe fn read_u32(data: &[u8]) -> u32 {
debug_assert!(data.len() >= 4);
let ptr = data.as_ptr() as *const u32;
ptr.read_unaligned()
}
/// 安全包装
pub fn safe_read_u32(data: &[u8]) -> Option<u32> {
if data.len() >= 4 {
Some(unsafe { read_u32(data) })
} else {
None
}
}
使用 debug_assert
pub fn get_unchecked<T>(slice: &[T], index: usize) -> &T {
// Release 模式下移除检查,Debug 模式保留
debug_assert!(index < slice.len(), "索引越界");
unsafe {
slice.get_unchecked(index)
}
}
安全抽象模式
/// 一个安全的固定大小环形缓冲区
pub struct RingBuffer<T, const N: usize> {
buffer: [std::mem::MaybeUninit<T>; N],
head: usize,
tail: usize,
len: usize,
}
impl<T, const N: usize> RingBuffer<T, N> {
pub fn new() -> Self {
Self {
buffer: unsafe { std::mem::MaybeUninit::uninit().assume_init() },
head: 0,
tail: 0,
len: 0,
}
}
// 公共 API 都是安全的
pub fn push(&mut self, item: T) -> Result<(), T> {
if self.len == N {
return Err(item);
}
// unsafe 被封装在内部
unsafe {
self.buffer[self.tail].as_mut_ptr().write(item);
}
self.tail = (self.tail + 1) % N;
self.len += 1;
Ok(())
}
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
let item = unsafe {
self.buffer[self.head].as_ptr().read()
};
self.head = (self.head + 1) % N;
self.len -= 1;
Some(item)
}
}
impl<T, const N: usize> Drop for RingBuffer<T, N> {
fn drop(&mut self) {
while self.pop().is_some() {}
}
}
速查表
| 操作 | 是否需要 unsafe |
|---|---|
| 创建裸指针 | ❌ 安全 |
| 解引用裸指针 | ✅ unsafe |
| 调用 unsafe fn | ✅ unsafe |
| 实现 unsafe trait | ✅ unsafe impl |
| 读写 static mut | ✅ unsafe |
| 读取 union 字段 | ✅ unsafe |
| FFI 函数调用 | ✅ unsafe |
| 内联汇编 | ✅ unsafe |
| transmute | ✅ unsafe |
| from_raw_parts | ✅ unsafe |
关键函数
// 类型转换
std::mem::transmute::<T, U>(value) // 重新解释位模式
std::mem::transmute_copy::<T, U>(&val) // 复制并转换
// 内存操作
std::ptr::read(ptr) // 从指针读取
std::ptr::write(ptr, val) // 向指针写入
std::ptr::copy(src, dst, count) // 复制内存(可重叠)
std::ptr::copy_nonoverlapping(...) // 复制内存(不可重叠)
std::ptr::swap(x, y) // 交换
std::ptr::drop_in_place(ptr) // 析构
// 切片
std::slice::from_raw_parts(ptr, len)
std::slice::from_raw_parts_mut(ptr, len)
Macro 宏编程
宏系统概览
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 宏系统 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 声明宏 (Declarative) │ 过程宏 (Procedural) │
│ macro_rules! │ │
│ ───────────────────────────────────────────────────────────── │
│ • 模式匹配 │ • 函数式宏 proc_macro │
│ • 代码模板替换 │ • derive 宏 proc_macro_derive │
│ • 编译时展开 │ • 属性宏 proc_macro_attribute│
│ │
├─────────────────────────────────────────────────────────────────────┤
│ 调用方式 │
├─────────────────────────────────────────────────────────────────────┤
│ macro!() macro![] macro!{} │
│ #[derive(Mac)] #[mac] #[mac(...)] │
└─────────────────────────────────────────────────────────────────────┘
宏 vs 函数
| 特性 | 宏 | 函数 |
|---|---|---|
| 执行时机 | 编译时 | 运行时 |
| 参数数量 | 可变 | 固定 |
| 类型检查 | 展开后检查 | 调用时检查 |
| 代码生成 | ✅ | ❌ |
| 调试难度 | 较高 | 较低 |
声明宏 (macro_rules!)
基础语法
// ============ 最简单的宏 ============
macro_rules! say_hello {
() => {
println!("Hello!");
};
}
// ============ 带参数的宏 ============
macro_rules! say {
($msg:expr) => {
println!("{}", $msg);
};
}
// ============ 多个分支 ============
macro_rules! greet {
() => {
println!("Hello, World!");
};
($name:expr) => {
println!("Hello, {}!", $name);
};
($name:expr, $times:expr) => {
for _ in 0..$times {
println!("Hello, {}!", $name);
}
};
}
fn main() {
say_hello!(); // Hello!
say!("Hi there"); // Hi there
greet!(); // Hello, World!
greet!("Alice"); // Hello, Alice!
greet!("Bob", 3); // Hello, Bob! (3次)
}
元变量类型 (Fragment Specifiers)
macro_rules! demo_fragments {
// ============ 常用类型 ============
// expr - 表达式
(expr: $e:expr) => { println!("expr: {:?}", $e) };
// ident - 标识符
(ident: $i:ident) => { let $i = 42; };
// ty - 类型
(ty: $t:ty) => { let _: $t; };
// pat - 模式
(pat: $p:pat) => { let $p = (1, 2); };
// stmt - 语句
(stmt: $s:stmt) => { $s };
// block - 代码块
(block: $b:block) => { $b };
// item - 项(函数、结构体等)
(item: $i:item) => { $i };
// path - 路径
(path: $p:path) => { type Alias = $p; };
// tt - 单个 token tree
(tt: $t:tt) => { $t };
// literal - 字面量
(lit: $l:literal) => { println!("{}", $l) };
// lifetime - 生命周期
(lt: $lt:lifetime) => { fn foo<$lt>() {} };
// meta - 属性内容
(meta: $m:meta) => { #[$m] struct S; };
// vis - 可见性
(vis: $v:vis) => { $v fn visible() {} };
}
fn main() {
demo_fragments!(expr: 1 + 2);
demo_fragments!(ident: my_var);
demo_fragments!(ty: Vec<i32>);
demo_fragments!(pat: (a, b));
demo_fragments!(stmt: let x = 5);
demo_fragments!(block: { println!("block"); });
demo_fragments!(lit: "hello");
}
重复模式
// ============ 基本重复 ============
macro_rules! vec_of_strings {
// $(...),* 表示零次或多次,用逗号分隔
($($x:expr),*) => {
vec![$($x.to_string()),*]
};
}
// ============ 至少一次 ============
macro_rules! sum {
// $(...),+ 表示一次或多次
($($x:expr),+) => {
{
let mut total = 0;
$(total += $x;)+
total
}
};
}
// ============ 零次或一次 ============
macro_rules! optional_param {
// $(...)? 表示零次或一次
($name:ident $(: $ty:ty)?) => {
// 如果提供了类型,使用它;否则使用默认类型
struct $name {
value: $($ty)?
}
};
}
// ============ 复杂重复模式 ============
macro_rules! hash_map {
// 键值对重复
($($key:expr => $value:expr),* $(,)?) => {{
let mut map = std::collections::HashMap::new();
$(map.insert($key, $value);)*
map
}};
}
fn main() {
let v = vec_of_strings!["a", "b", "c"];
println!("{:?}", v); // ["a", "b", "c"]
let total = sum!(1, 2, 3, 4, 5);
println!("sum: {}", total); // 15
let map = hash_map! {
"one" => 1,
"two" => 2,
"three" => 3, // 尾随逗号也支持
};
println!("{:?}", map);
}
实用宏示例
// ============ 1. 简化 println! 调试 ============
macro_rules! dbg_vars {
($($var:ident),+ $(,)?) => {
$(
println!("{} = {:?}", stringify!($var), $var);
)+
};
}
// ============ 2. 创建枚举及其字符串转换 ============
macro_rules! define_enum {
(
$(#[$meta:meta])*
$vis:vis enum $name:ident {
$($variant:ident),* $(,)?
}
) => {
$(#[$meta])*
$vis enum $name {
$($variant),*
}
impl $name {
pub fn as_str(&self) -> &'static str {
match self {
$(Self::$variant => stringify!($variant)),*
}
}
pub fn variants() -> &'static [&'static str] {
&[$(stringify!($variant)),*]
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
};
}
define_enum! {
#[derive(Debug, Clone, Copy)]
pub enum Color {
Red,
Green,
Blue,
}
}
// ============ 3. 构建器模式生成 ============
macro_rules! builder {
(
$struct_name:ident {
$($field:ident : $type:ty),* $(,)?
}
) => {
pub struct $struct_name {
$($field: $type),*
}
paste::paste! {
pub struct [<$struct_name Builder>] {
$($field: Option<$type>),*
}
impl [<$struct_name Builder>] {
pub fn new() -> Self {
Self {
$($field: None),*
}
}
$(
pub fn $field(mut self, value: $type) -> Self {
self.$field = Some(value);
self
}
)*
pub fn build(self) -> Result<$struct_name, &'static str> {
Ok($struct_name {
$($field: self.$field.ok_or(concat!(stringify!($field), " is required"))?),*
})
}
}
}
};
}
// ============ 4. 测试夹具 ============
macro_rules! test_cases {
($test_fn:ident: $($name:ident => ($($input:expr),*) == $expected:expr);+ $(;)?) => {
$(
#[test]
fn $name() {
assert_eq!($test_fn($($input),*), $expected);
}
)+
};
}
fn add(a: i32, b: i32) -> i32 { a + b }
test_cases! {
add:
test_add_positive => (2, 3) == 5;
test_add_negative => (-1, -1) == -2;
test_add_zero => (0, 0) == 0;
}
fn main() {
let x = 42;
let name = "Alice";
dbg_vars!(x, name);
let color = Color::Red;
println!("{}", color); // Red
println!("{:?}", Color::variants()); // ["Red", "Green", "Blue"]
}
递归宏
// ============ 计算参数数量 ============
macro_rules! count {
() => { 0usize };
($head:tt $($tail:tt)*) => { 1usize + count!($($tail)*) };
}
// ============ 元组索引生成 ============
macro_rules! tuple_index {
($tuple:expr; $($idx:tt),+ $(,)?) => {
($($tuple.$idx),+)
};
}
// ============ 递归展开列表 ============
macro_rules! nested_vec {
// 基础情况:单个元素
($elem:expr) => { $elem };
// 递归情况:多个元素
($head:expr, $($tail:expr),+) => {
vec![nested_vec!($head), nested_vec!($($tail),+)]
};
}
// ============ 实现 trait 给多个类型 ============
macro_rules! impl_trait_for {
($trait_name:ident for $($ty:ty),+ $(,)?) => {
$(
impl $trait_name for $ty {}
)+
};
}
trait Printable {}
impl_trait_for!(Printable for i32, i64, f32, f64, String);
fn main() {
let n = count!(a b c d e);
println!("count: {}", n); // 5
let tuple = (1, "hello", 3.14, true);
let selected = tuple_index!(tuple; 0, 2);
println!("{:?}", selected); // (1, 3.14)
}
宏的卫生性 (Hygiene)
// ============ 标识符卫生性 ============
macro_rules! using_a {
($e:expr) => {
{
let a = 42; // 这个 a 在宏内部,外部看不到
$e // $e 中的 a 不会被替换
}
};
}
fn main() {
let a = 10;
let result = using_a!(a * 2); // 使用外部的 a
println!("{}", result); // 20,不是 84
}
// ============ 突破卫生性 ============
macro_rules! declare_var {
($name:ident, $value:expr) => {
let $name = $value; // $name 来自调用者,可被外部访问
};
}
fn main() {
declare_var!(x, 100);
println!("{}", x); // 100 - 可以访问
}
过程宏 (Procedural Macros)
项目结构
my_macros/
├── Cargo.toml
└── src/
└── lib.rs
my_app/
├── Cargo.toml
└── src/
└── main.rs
# my_macros/Cargo.toml
[package]
name = "my_macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true # 声明为过程宏 crate
[dependencies]
syn = { version = "2", features = ["full"] }
quote = "1"
proc-macro2 = "1"
函数式过程宏
// my_macros/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, LitStr};
// ============ 简单示例 ============
#[proc_macro]
pub fn make_hello(input: TokenStream) -> TokenStream {
let name = parse_macro_input!(input as LitStr);
let name_value = name.value();
let expanded = quote! {
fn hello() {
println!("Hello, {}!", #name_value);
}
};
expanded.into()
}
// ============ SQL 查询生成器 ============
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
let input_str = input.to_string();
// 简单验证 SQL(实际应用中会更复杂)
if !input_str.to_uppercase().starts_with("SELECT") {
return syn::Error::new(
proc_macro2::Span::call_site(),
"SQL must start with SELECT"
)
.to_compile_error()
.into();
}
let expanded = quote! {
{
let query = #input_str;
query
}
};
expanded.into()
}
// ============ 解析自定义语法 ============
use syn::parse::{Parse, ParseStream};
use syn::{Ident, Token, Expr};
struct MathExpr {
left: Expr,
op: Ident,
right: Expr,
}
impl Parse for MathExpr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let left = input.parse()?;
input.parse::<Token![,]>()?;
let op = input.parse()?;
input.parse::<Token![,]>()?;
let right = input.parse()?;
Ok(MathExpr { left, op, right })
}
}
#[proc_macro]
pub fn math(input: TokenStream) -> TokenStream {
let MathExpr { left, op, right } = parse_macro_input!(input as MathExpr);
let op_str = op.to_string();
let expanded = match op_str.as_str() {
"add" => quote! { #left + #right },
"sub" => quote! { #left - #right },
"mul" => quote! { #left * #right },
"div" => quote! { #left / #right },
_ => {
return syn::Error::new(op.span(), "Unknown operation")
.to_compile_error()
.into();
}
};
expanded.into()
}
// my_app/src/main.rs
use my_macros::{make_hello, sql, math};
make_hello!("World");
fn main() {
hello(); // Hello, World!
let query = sql!(SELECT * FROM users WHERE id = 1);
println!("{}", query);
let result = math!(10, add, 5);
println!("{}", result); // 15
}
Derive 宏
// my_macros/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields};
// ============ 基础 Derive 宏 ============
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let expanded = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, Macro! My name is {}", stringify!(#name));
}
}
};
expanded.into()
}
// ============ 带属性的 Derive 宏 ============
#[proc_macro_derive(Builder, attributes(builder))]
pub fn builder_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let builder_name = syn::Ident::new(
&format!("{}Builder", name),
name.span()
);
let fields = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => &fields.named,
_ => panic!("Only named fields are supported"),
},
_ => panic!("Only structs are supported"),
};
// 生成 builder 字段
let builder_fields = fields.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;
quote! { #name: Option<#ty> }
});
// 生成 setter 方法
let setters = fields.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;
quote! {
pub fn #name(mut self, value: #ty) -> Self {
self.#name = Some(value);
self
}
}
});
// 生成 build 方法
let build_fields = fields.iter().map(|f| {
let name = &f.ident;
quote! {
#name: self.#name.ok_or(concat!(stringify!(#name), " is not set"))?
}
});
// 生成初始化
let init_fields = fields.iter().map(|f| {
let name = &f.ident;
quote! { #name: None }
});
let expanded = quote! {
pub struct #builder_name {
#(#builder_fields),*
}
impl #builder_name {
#(#setters)*
pub fn build(self) -> Result<#name, &'static str> {
Ok(#name {
#(#build_fields),*
})
}
}
impl #name {
pub fn builder() -> #builder_name {
#builder_name {
#(#init_fields),*
}
}
}
};
expanded.into()
}
// ============ 自动实现 Debug(简化版)============
#[proc_macro_derive(MyDebug)]
pub fn my_debug_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let fields = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => fields.named.iter().map(|f| {
let name = &f.ident;
quote! {
.field(stringify!(#name), &self.#name)
}
}).collect::<Vec<_>>(),
Fields::Unnamed(fields) => fields.unnamed.iter().enumerate().map(|(i, _)| {
let index = syn::Index::from(i);
quote! {
.field(&self.#index)
}
}).collect::<Vec<_>>(),
Fields::Unit => vec![],
},
_ => panic!("Only structs are supported"),
};
let expanded = quote! {
impl std::fmt::Debug for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(#name))
#(#fields)*
.finish()
}
}
};
expanded.into()
}
// 使用
use my_macros::{HelloMacro, Builder, MyDebug};
trait HelloMacro {
fn hello_macro();
}
#[derive(HelloMacro)]
struct Pancakes;
#[derive(Builder)]
struct Command {
executable: String,
args: Vec<String>,
current_dir: String,
}
#[derive(MyDebug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
Pancakes::hello_macro();
let cmd = Command::builder()
.executable("cargo".to_string())
.args(vec!["build".to_string()])
.current_dir(".".to_string())
.build()
.unwrap();
let point = Point { x: 10, y: 20 };
println!("{:?}", point);
}
属性宏
// my_macros/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn, AttributeArgs, NestedMeta, Lit, Meta};
// ============ 简单函数包装 ============
#[proc_macro_attribute]
pub fn log_call(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let fn_block = &input.block;
let fn_sig = &input.sig;
let fn_vis = &input.vis;
let expanded = quote! {
#fn_vis #fn_sig {
println!("[LOG] Entering {}", stringify!(#fn_name));
let result = (|| #fn_block)();
println!("[LOG] Exiting {}", stringify!(#fn_name));
result
}
};
expanded.into()
}
// ============ 带参数的属性宏 ============
#[proc_macro_attribute]
pub fn retry(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as AttributeArgs);
let input = parse_macro_input!(item as ItemFn);
// 解析重试次数
let mut retries = 3usize;
for arg in args {
if let NestedMeta::Meta(Meta::NameValue(nv)) = arg {
if nv.path.is_ident("times") {
if let Lit::Int(lit) = nv.lit {
retries = lit.base10_parse().unwrap();
}
}
}
}
let fn_name = &input.sig.ident;
let fn_block = &input.block;
let fn_sig = &input.sig;
let fn_vis = &input.vis;
let expanded = quote! {
#fn_vis #fn_sig {
let mut attempts = 0;
loop {
attempts += 1;
let result = (|| #fn_block)();
if result.is_ok() || attempts >= #retries {
return result;
}
println!("[RETRY] {} attempt {} failed", stringify!(#fn_name), attempts);
}
}
};
expanded.into()
}
// ============ 路由宏(Web 框架风格)============
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr_str = attr.to_string();
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
// 解析路由如: "GET", "/users"
let parts: Vec<&str> = attr_str.split(',').map(|s| s.trim().trim_matches('"')).collect();
let method = parts.get(0).unwrap_or(&"GET");
let path = parts.get(1).unwrap_or(&"/");
let expanded = quote! {
#input
inventory::submit! {
Route {
method: #method,
path: #path,
handler: #fn_name,
}
}
};
expanded.into()
}
// ============ 测试标记宏 ============
#[proc_macro_attribute]
pub fn test_case(attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let fn_block = &input.block;
// 创建测试函数
let test_name = syn::Ident::new(
&format!("test_{}", fn_name),
fn_name.span()
);
let expanded = quote! {
#input
#[test]
fn #test_name() {
#fn_block
}
};
expanded.into()
}
// 使用
use my_macros::{log_call, retry, route};
#[log_call]
fn compute(x: i32) -> i32 {
x * 2
}
#[retry(times = 5)]
fn fetch_data() -> Result<String, &'static str> {
// 可能失败的操作
Err("network error")
}
#[route("GET", "/users")]
fn get_users() -> Vec<String> {
vec!["Alice".to_string(), "Bob".to_string()]
}
fn main() {
compute(21);
// [LOG] Entering compute
// [LOG] Exiting compute
}
syn 和 quote 深入
syn - 解析 Rust 代码
use syn::{
parse_macro_input, DeriveInput, Data, Fields, Type,
Attribute, Expr, Lit, Meta, NestedMeta,
ItemStruct, ItemFn, ItemEnum,
Generics, GenericParam, TypeParam,
};
// ============ 解析结构体 ============
fn parse_struct(input: DeriveInput) {
let name = input.ident;
let generics = input.generics;
let attrs = input.attrs;
// 处理泛型
for param in generics.params {
match param {
GenericParam::Type(TypeParam { ident, .. }) => {
println!("Type param: {}", ident);
}
GenericParam::Lifetime(lt) => {
println!("Lifetime: {}", lt.lifetime);
}
GenericParam::Const(c) => {
println!("Const: {}", c.ident);
}
}
}
// 处理字段
if let Data::Struct(data) = input.data {
match data.fields {
Fields::Named(fields) => {
for field in fields.named {
let field_name = field.ident.unwrap();
let field_type = field.ty;
println!("Field: {} : {:?}", field_name, field_type);
}
}
Fields::Unnamed(fields) => {
for (i, field) in fields.unnamed.iter().enumerate() {
println!("Field {}: {:?}", i, field.ty);
}
}
Fields::Unit => println!("Unit struct"),
}
}
}
// ============ 解析属性 ============
fn parse_attributes(attrs: &[Attribute]) {
for attr in attrs {
// #[derive(...)]
if attr.path.is_ident("derive") {
println!("Found derive attribute");
}
// #[my_attr(key = "value")]
if attr.path.is_ident("my_attr") {
if let Ok(Meta::List(list)) = attr.parse_meta() {
for nested in list.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = nested {
if nv.path.is_ident("key") {
if let Lit::Str(s) = nv.lit {
println!("key = {}", s.value());
}
}
}
}
}
}
}
}
// ============ 类型分析 ============
fn analyze_type(ty: &Type) {
match ty {
Type::Path(type_path) => {
if let Some(segment) = type_path.path.segments.last() {
println!("Type: {}", segment.ident);
// 检查是否是 Option<T>
if segment.ident == "Option" {
println!("This is an Option type");
}
}
}
Type::Reference(reference) => {
println!("Reference type, mutable: {}", reference.mutability.is_some());
}
Type::Array(array) => {
println!("Array type");
}
_ => {}
}
}
quote - 生成代码
use quote::{quote, quote_spanned, format_ident, ToTokens};
use proc_macro2::{Span, TokenStream};
// ============ 基础用法 ============
fn basic_quote() -> TokenStream {
let name = format_ident!("MyStruct");
let field = format_ident!("value");
let ty = quote! { i32 };
quote! {
struct #name {
#field: #ty,
}
}
}
// ============ 重复展开 ============
fn quote_repetition(names: &[&str]) -> TokenStream {
let fields = names.iter().map(|name| {
let ident = format_ident!("{}", name);
quote! { #ident: String }
});
let inits = names.iter().map(|name| {
let ident = format_ident!("{}", name);
quote! { #ident: String::new() }
});
quote! {
struct MyStruct {
#(#fields),*
}
impl Default for MyStruct {
fn default() -> Self {
Self {
#(#inits),*
}
}
}
}
}
// ============ 条件生成 ============
fn conditional_quote(include_debug: bool) -> TokenStream {
let debug_impl = if include_debug {
Some(quote! {
impl std::fmt::Debug for MyStruct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MyStruct")
}
}
})
} else {
None
};
quote! {
struct MyStruct;
#debug_impl
}
}
// ============ 保留错误位置 ============
fn quote_with_span(field_name: &syn::Ident, field_type: &syn::Type) -> TokenStream {
// 错误消息会指向原始代码位置
quote_spanned! {field_name.span()=>
impl HasField for MyStruct {
type FieldType = #field_type;
fn get_field(&self) -> &Self::FieldType {
&self.#field_name
}
}
}
}
// ============ 嵌套 quote ============
fn nested_quote() -> TokenStream {
let inner = quote! { println!("inner") };
let outer = quote! {
fn wrapper() {
#inner
}
};
outer
}
高级模式与技巧
TT Muncher(Token 咀嚼器)
// 逐个处理 token
macro_rules! parse_commands {
// 基础情况:空输入
() => {};
// SET key = value
(SET $key:ident = $value:expr; $($rest:tt)*) => {
println!("Setting {} = {:?}", stringify!($key), $value);
parse_commands!($($rest)*);
};
// GET key
(GET $key:ident; $($rest:tt)*) => {
println!("Getting {}", stringify!($key));
parse_commands!($($rest)*);
};
// DELETE key
(DELETE $key:ident; $($rest:tt)*) => {
println!("Deleting {}", stringify!($key));
parse_commands!($($rest)*);
};
}
fn main() {
parse_commands! {
SET name = "Alice";
SET age = 30;
GET name;
DELETE age;
}
}
Push-Down Accumulator(下推累加器)
// 累积处理结果
macro_rules! reverse {
// 入口
([$($input:tt)*]) => {
reverse!([$($input)*] -> [])
};
// 基础情况:输入空,返回累积结果
([] -> [$($output:tt)*]) => {
($($output)*)
};
// 递归:从输入取一个,放到输出前面
([$head:tt $($tail:tt)*] -> [$($output:tt)*]) => {
reverse!([$($tail)*] -> [$head $($output)*])
};
}
fn main() {
let reversed = reverse!([1 2 3 4 5]);
// 展开为 (5 4 3 2 1)
}
// 更实用的例子:生成嵌套结构
macro_rules! nested {
// 入口
($($items:tt)*) => {
nested!(@build [] $($items)*)
};
// 基础情况
(@build [$($acc:tt)*]) => {
$($acc)*
};
// 递归
(@build [$($acc:tt)*] $item:tt $($rest:tt)*) => {
nested!(@build [{ $item $($acc)* }] $($rest)*)
};
}
内部规则模式
macro_rules! complex_macro {
// 公共入口
($($input:tt)*) => {
complex_macro!(@parse $($input)*)
};
// 内部规则:解析
(@parse struct $name:ident { $($fields:tt)* }) => {
complex_macro!(@gen_struct $name [] $($fields)*)
};
// 内部规则:生成结构体
(@gen_struct $name:ident [$($processed:tt)*]) => {
struct $name {
$($processed)*
}
};
// 内部规则:处理字段
(@gen_struct $name:ident [$($processed:tt)*] $field:ident : $ty:ty, $($rest:tt)*) => {
complex_macro!(@gen_struct $name [$($processed)* $field: $ty,] $($rest)*)
};
(@gen_struct $name:ident [$($processed:tt)*] $field:ident : $ty:ty) => {
complex_macro!(@gen_struct $name [$($processed)* $field: $ty,])
};
}
complex_macro! {
struct User {
name: String,
age: u32
}
}
Callback 模式
// 宏调用另一个宏处理结果
macro_rules! call_with_result {
($callback:ident, $($input:tt)*) => {
$callback!([ processed: $($input)* ])
};
}
macro_rules! my_callback {
([ processed: $($data:tt)* ]) => {
println!("Received: {}", stringify!($($data)*));
};
}
fn main() {
call_with_result!(my_callback, hello world);
}
调试宏
展开宏
# 使用 cargo-expand
cargo install cargo-expand
cargo expand
# 展开特定模块
cargo expand module_name
# 展开特定测试
cargo expand --test test_name
编译时调试
macro_rules! debug_macro {
($($tokens:tt)*) => {
// 编译时打印输入
compile_error!(concat!("Input: ", stringify!($($tokens)*)));
};
}
// 使用 trace_macros(nightly)
#![feature(trace_macros)]
trace_macros!(true);
my_macro!(some input);
trace_macros!(false);
// 使用 log_syntax(nightly)
#![feature(log_syntax)]
macro_rules! debug {
($($t:tt)*) => {
log_syntax!($($t)*);
};
}
过程宏调试
#[proc_macro_derive(MyDerive)]
pub fn my_derive(input: TokenStream) -> TokenStream {
// 打印输入
eprintln!("Input: {:#?}", input);
let input = parse_macro_input!(input as DeriveInput);
// 打印解析后的 AST
eprintln!("Parsed: {:#?}", input);
let expanded = quote! { /* ... */ };
// 打印输出
eprintln!("Output: {}", expanded);
expanded.into()
}
实战示例
完整的 Derive 宏:自动实现 From
// lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields, Type, Path};
#[proc_macro_derive(AutoFrom, attributes(from))]
pub fn auto_from_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let Data::Struct(data) = &input.data else {
return syn::Error::new_spanned(&input, "AutoFrom only works on structs")
.to_compile_error()
.into();
};
let Fields::Named(fields) = &data.fields else {
return syn::Error::new_spanned(&input, "AutoFrom requires named fields")
.to_compile_error()
.into();
};
// 查找带有 #[from] 属性的字段
let mut from_impls = Vec::new();
for field in &fields.named {
let field_name = field.ident.as_ref().unwrap();
let field_type = &field.ty;
// 检查 #[from] 属性
let has_from = field.attrs.iter().any(|attr| attr.path.is_ident("from"));
if has_from {
// 生成所有其他字段的默认值
let other_fields = fields.named.iter()
.filter(|f| f.ident.as_ref() != Some(field_name))
.map(|f| {
let name = &f.ident;
quote! { #name: Default::default() }
});
from_impls.push(quote! {
impl From<#field_type> for #name {
fn from(value: #field_type) -> Self {
Self {
#field_name: value,
#(#other_fields),*
}
}
}
});
}
}
let expanded = quote! {
#(#from_impls)*
};
expanded.into()
}
// 使用
use my_macros::AutoFrom;
#[derive(AutoFrom, Default, Debug)]
struct User {
#[from]
name: String,
#[from]
age: u32,
active: bool,
}
fn main() {
let user1: User = "Alice".to_string().into();
let user2: User = 25u32.into();
println!("{:?}", user1); // User { name: "Alice", age: 0, active: false }
println!("{:?}", user2); // User { name: "", age: 25, active: false }
}
DSL:查询构建器
// 声明宏版本
macro_rules! query {
(
SELECT $($col:ident),+
FROM $table:ident
$(WHERE $($cond:tt)+)?
$(ORDER BY $order_col:ident $order_dir:ident)?
$(LIMIT $limit:expr)?
) => {{
let mut q = format!(
"SELECT {} FROM {}",
vec![$(stringify!($col)),+].join(", "),
stringify!($table)
);
$(
q.push_str(&format!(" WHERE {}", stringify!($($cond)+)));
)?
$(
q.push_str(&format!(" ORDER BY {} {}",
stringify!($order_col),
stringify!($order_dir)
));
)?
$(
q.push_str(&format!(" LIMIT {}", $limit));
)?
q
}};
}
fn main() {
let q1 = query!(
SELECT id, name, email
FROM users
WHERE id > 10 AND active = true
ORDER BY name ASC
LIMIT 10
);
println!("{}", q1);
// SELECT id, name, email FROM users WHERE id > 10 AND active = true ORDER BY name ASC LIMIT 10
}
类型安全的 HTML 构建器
macro_rules! html {
// 文本节点
($text:literal) => {
$text.to_string()
};
// 空元素
($tag:ident) => {
format!("<{}></{}>", stringify!($tag), stringify!($tag))
};
// 带属性的元素
($tag:ident [$($attr:ident = $value:expr),* $(,)?]) => {
format!(
"<{} {}></{}>",
stringify!($tag),
vec![$(format!("{}=\"{}\"", stringify!($attr), $value)),*].join(" "),
stringify!($tag)
)
};
// 带子元素
($tag:ident { $($child:tt)* }) => {
format!(
"<{}>{}</{}>",
stringify!($tag),
html!(@children $($child)*),
stringify!($tag)
)
};
// 带属性和子元素
($tag:ident [$($attr:ident = $value:expr),* $(,)?] { $($child:tt)* }) => {
format!(
"<{} {}>{}</{}>",
stringify!($tag),
vec![$(format!("{}=\"{}\"", stringify!($attr), $value)),*].join(" "),
html!(@children $($child)*),
stringify!($tag)
)
};
// 内部规则:处理子元素
(@children) => { String::new() };
(@children $tag:ident $($rest:tt)*) => {
format!("{}{}", html!($tag), html!(@children $($rest)*))
};
(@children $tag:ident [$($attr:tt)*] $($rest:tt)*) => {
format!("{}{}", html!($tag [$($attr)*]), html!(@children $($rest)*))
};
(@children $tag:ident { $($inner:tt)* } $($rest:tt)*) => {
format!("{}{}", html!($tag { $($inner)* }), html!(@children $($rest)*))
};
(@children $tag:ident [$($attr:tt)*] { $($inner:tt)* } $($rest:tt)*) => {
format!("{}{}", html!($tag [$($attr)*] { $($inner)* }), html!(@children $($rest)*))
};
(@children $text:literal $($rest:tt)*) => {
format!("{}{}", $text, html!(@children $($rest)*))
};
}
fn main() {
let page = html!(
html {
head {
title { "My Page" }
}
body {
h1 [class = "title"] { "Hello" }
div [id = "content", class = "container"] {
p { "Welcome to my page!" }
a [href = "https://example.com"] { "Click here" }
}
}
}
);
println!("{}", page);
}
常用宏 Crate
| Crate | 用途 |
|---|---|
syn |
解析 Rust 代码 |
quote |
生成 Rust 代码 |
proc-macro2 |
TokenStream 处理 |
darling |
简化属性解析 |
paste |
标识符拼接 |
thiserror |
错误类型派生 |
serde |
序列化派生 |
darling 示例
use darling::{FromDeriveInput, FromField};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[derive(FromDeriveInput)]
#[darling(attributes(my_attr))]
struct MyInputReceiver {
ident: syn::Ident,
data: darling::ast::Data<(), MyFieldReceiver>,
#[darling(default)]
rename: Option<String>,
}
#[derive(FromField)]
#[darling(attributes(my_attr))]
struct MyFieldReceiver {
ident: Option<syn::Ident>,
ty: syn::Type,
#[darling(default)]
skip: bool,
}
#[proc_macro_derive(MyDerive, attributes(my_attr))]
pub fn my_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let receiver = MyInputReceiver::from_derive_input(&input).unwrap();
// 现在可以直接访问解析后的属性
let name = &receiver.ident;
let rename = receiver.rename.as_ref().map(|r| quote! { #r }).unwrap_or(quote! { stringify!(#name) });
quote! {
impl Named for #name {
fn name(&self) -> &'static str {
#rename
}
}
}.into()
}
速查表
声明宏元变量
| 类型 | 说明 | 示例 |
|---|---|---|
expr |
表达式 | 1 + 2, foo() |
ident |
标识符 | foo, x |
ty |
类型 | i32, Vec<T> |
pat |
模式 | Some(x), _ |
stmt |
语句 | let x = 1; |
block |
代码块 | { ... } |
item |
项 | fn foo() {} |
path |
路径 | std::io::Result |
tt |
Token Tree | 任何单个 token |
literal |
字面量 | "hello", 42 |
lifetime |
生命周期 | 'a |
meta |
属性元数据 | derive(Debug) |
vis |
可见性 | pub, pub(crate) |
重复符号
| 符号 | 含义 |
|---|---|
$(...)* |
零次或多次 |
$(...)+ |
一次或多次 |
$(...)? |
零次或一次 |
过程宏类型
| 类型 | 属性 | 用途 |
|---|---|---|
| 函数式 | #[proc_macro] |
自定义语法 |
| Derive | #[proc_macro_derive] |
自动实现 trait |
| 属性 | #[proc_macro_attribute] |
修改/增强代码 |
自动化测试
测试体系概览
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 测试金字塔 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ E2E │ ← 端到端测试(最少) │
│ ┌─┴─────────┴─┐ │
│ │ 集成测试 │ ← tests/ 目录 │
│ ┌─┴─────────────┴─┐ │
│ │ 单元测试 │ ← #[cfg(test)] mod tests │
│ ┌─┴─────────────────┴─┐ │
│ │ 文档测试 │ ← /// ``` 代码块 │
│ └─────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ 项目结构 │
├─────────────────────────────────────────────────────────────────────┤
│ my_project/ │
│ ├── Cargo.toml │
│ ├── src/ │
│ │ ├── lib.rs ← 单元测试 + 文档测试 │
│ │ └── main.rs │
│ ├── tests/ ← 集成测试 │
│ │ ├── integration_test.rs │
│ │ └── common/mod.rs ← 测试辅助模块 │
│ └── benches/ ← 基准测试 │
│ └── benchmark.rs │
└─────────────────────────────────────────────────────────────────────┘
单元测试基础
基本语法
// src/lib.rs
/// 加法函数
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
/// 除法函数,除数为零时返回 None
pub fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
// ============ 测试模块 ============
#[cfg(test)] // 仅在 cargo test 时编译
mod tests {
use super::*; // 导入父模块所有内容
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, -1), -2);
}
#[test]
fn test_divide() {
assert_eq!(divide(10.0, 2.0), Some(5.0));
}
#[test]
fn test_divide_by_zero() {
assert_eq!(divide(10.0, 0.0), None);
}
}
断言宏
#[cfg(test)]
mod tests {
#[test]
fn test_assertions() {
// ============ 基本断言 ============
assert!(true);
assert!(1 + 1 == 2);
// ============ 相等性断言 ============
assert_eq!(4, 2 + 2); // 相等
assert_ne!(4, 2 + 1); // 不相等
// ============ 带自定义消息 ============
let x = 5;
assert!(x > 0, "x should be positive, but got {}", x);
assert_eq!(x, 5, "Expected 5, got {}", x);
// ============ 浮点数比较(注意精度问题)============
let a = 0.1 + 0.2;
let b = 0.3;
// assert_eq!(a, b); // ❌ 可能失败
assert!((a - b).abs() < f64::EPSILON); // ✅ 正确方式
// ============ 使用 approx crate ============
// assert_relative_eq!(a, b, epsilon = 1e-10);
}
// ============ debug_assert! 系列(仅 debug 模式)============
#[test]
fn test_debug_assertions() {
debug_assert!(true);
debug_assert_eq!(1, 1);
debug_assert_ne!(1, 2);
}
}
测试属性
#[cfg(test)]
mod tests {
// ============ 预期 panic ============
#[test]
#[should_panic]
fn test_panic() {
panic!("This test should panic");
}
// 指定 panic 消息
#[test]
#[should_panic(expected = "divide by zero")]
fn test_panic_message() {
panic!("divide by zero error");
}
// ============ 忽略测试 ============
#[test]
#[ignore]
fn expensive_test() {
// 耗时测试,默认跳过
std::thread::sleep(std::time::Duration::from_secs(10));
}
// 带原因的忽略
#[test]
#[ignore = "需要外部服务"]
fn test_external_service() {
// ...
}
// ============ 返回 Result ============
#[test]
fn test_with_result() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err("math is broken".to_string())
}
}
// 使用 ? 操作符
#[test]
fn test_with_question_mark() -> Result<(), Box<dyn std::error::Error>> {
let value: i32 = "42".parse()?;
assert_eq!(value, 42);
Ok(())
}
}
测试私有函数
// src/lib.rs
fn internal_add(a: i32, b: i32) -> i32 {
a + b
}
pub fn public_add(a: i32, b: i32) -> i32 {
internal_add(a, b)
}
#[cfg(test)]
mod tests {
use super::*;
// ✅ 可以测试私有函数
#[test]
fn test_internal_add() {
assert_eq!(internal_add(1, 2), 3);
}
}
集成测试
基本结构
// tests/integration_test.rs
// 作为外部 crate 使用,只能访问 pub 接口
use my_project::{add, divide};
#[test]
fn test_add_integration() {
assert_eq!(add(10, 20), 30);
}
#[test]
fn test_divide_integration() {
assert_eq!(divide(100.0, 4.0), Some(25.0));
}
// 可以有多个测试文件
// tests/another_test.rs
// tests/feature_a_test.rs
共享测试辅助代码
// tests/common/mod.rs(不会被当作测试文件)
pub struct TestContext {
pub temp_dir: std::path::PathBuf,
}
impl TestContext {
pub fn new() -> Self {
let temp_dir = std::env::temp_dir().join(format!("test_{}", rand::random::<u32>()));
std::fs::create_dir_all(&temp_dir).unwrap();
TestContext { temp_dir }
}
pub fn create_file(&self, name: &str, content: &str) -> std::path::PathBuf {
let path = self.temp_dir.join(name);
std::fs::write(&path, content).unwrap();
path
}
}
impl Drop for TestContext {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.temp_dir);
}
}
pub fn setup() {
// 初始化日志等
}
// tests/file_test.rs
mod common;
use common::TestContext;
#[test]
fn test_file_operations() {
common::setup();
let ctx = TestContext::new();
let file = ctx.create_file("test.txt", "hello");
assert!(file.exists());
}
子目录组织
tests/
├── api/
│ ├── mod.rs ← 声明子模块
│ ├── users_test.rs
│ └── orders_test.rs
├── common/
│ └── mod.rs
└── api_tests.rs ← 入口文件
// tests/api_tests.rs
mod common;
mod api;
// tests/api/mod.rs
mod users_test;
mod orders_test;
// tests/api/users_test.rs
use crate::common::TestContext;
#[test]
fn test_create_user() {
// ...
}
文档测试
基本文档测试
/// 计算两个数的和
///
/// # Examples
///
/// ```
/// use my_project::add;
///
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
/// 安全除法
///
/// # Examples
///
/// ```
/// use my_project::divide;
///
/// // 正常情况
/// assert_eq!(divide(10.0, 2.0), Some(5.0));
///
/// // 除以零
/// assert_eq!(divide(10.0, 0.0), None);
/// ```
///
/// # Panics
///
/// 此函数不会 panic。
pub fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 { None } else { Some(a / b) }
}
文档测试属性
/// 示例函数
///
/// # Examples
///
/// 基本用法:
/// ```
/// assert_eq!(2 + 2, 4);
/// ```
///
/// 隐藏设置代码(不显示在文档中):
/// ```
/// # fn setup() -> i32 { 42 }
/// # let value = setup();
/// assert_eq!(value, 42);
/// ```
///
/// 标记为应该 panic:
/// ```should_panic
/// panic!("This panics!");
/// ```
///
/// 编译但不运行:
/// ```no_run
/// loop {
/// // 无限循环
/// }
/// ```
///
/// 忽略此代码块:
/// ```ignore
/// 这不是有效的 Rust 代码
/// ```
///
/// 标记为编译失败:
/// ```compile_fail
/// let x: i32 = "hello";
/// ```
///
/// 指定 Rust 版本:
/// ```edition2021
/// // Rust 2021 特性
/// ```
pub fn example() {}
文档测试中使用 ? 操作符
/// 解析配置
///
/// # Examples
///
/// ```
/// use std::error::Error;
///
/// fn main() -> Result<(), Box<dyn Error>> {
/// let config = my_project::parse_config("key=value")?;
/// assert_eq!(config.get("key"), Some(&"value".to_string()));
/// Ok(())
/// }
/// ```
///
/// 或者更简洁:
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let config = my_project::parse_config("key=value")?;
/// assert_eq!(config.get("key"), Some(&"value".to_string()));
/// # Ok(())
/// # }
/// ```
pub fn parse_config(input: &str) -> Result<std::collections::HashMap<String, String>, &'static str> {
// ...
Ok(std::collections::HashMap::new())
}
运行测试
cargo test 命令
# 运行所有测试
cargo test
# 运行特定测试
cargo test test_name
cargo test test_add # 匹配名称包含 "test_add" 的测试
# 运行特定模块的测试
cargo test tests:: # 运行 tests 模块下的所有测试
# 运行特定测试文件
cargo test --test integration_test
# 运行文档测试
cargo test --doc
# 只运行单元测试(不运行集成测试和文档测试)
cargo test --lib
# 运行被忽略的测试
cargo test -- --ignored
# 运行所有测试(包括忽略的)
cargo test -- --include-ignored
# 显示测试输出(默认隐藏成功测试的 println!)
cargo test -- --nocapture
# 单线程运行(避免并发问题)
cargo test -- --test-threads=1
# 显示测试列表
cargo test -- --list
# 只编译不运行
cargo test --no-run
# Release 模式测试
cargo test --release
# 显示更多信息
cargo test -- --show-output
测试过滤
#[cfg(test)]
mod tests {
#[test]
fn test_feature_a_basic() {}
#[test]
fn test_feature_a_advanced() {}
#[test]
fn test_feature_b_basic() {}
#[test]
#[ignore]
fn test_slow() {}
}
# 运行包含 "feature_a" 的测试
cargo test feature_a
# 运行精确匹配的测试
cargo test -- --exact test_feature_a_basic
# 排除某些测试
cargo test -- --skip feature_b
# 组合过滤
cargo test feature_a -- --skip advanced
异步测试
使用 tokio
[dev-dependencies]
tokio = { version = "1", features = ["full", "test-util"] }
// 使用 tokio::test 宏
#[tokio::test]
async fn test_async_function() {
let result = async_add(1, 2).await;
assert_eq!(result, 3);
}
async fn async_add(a: i32, b: i32) -> i32 {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
a + b
}
// 配置运行时
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_multi_thread() {
// 多线程运行时
}
#[tokio::test(flavor = "current_thread")]
async fn test_single_thread() {
// 单线程运行时
}
// 带超时
#[tokio::test]
async fn test_with_timeout() {
let result = tokio::time::timeout(
std::time::Duration::from_secs(5),
async_operation()
).await;
assert!(result.is_ok());
}
async fn async_operation() -> i32 {
42
}
使用 async-std
[dev-dependencies]
async-std = { version = "1", features = ["attributes"] }
#[async_std::test]
async fn test_with_async_std() {
let result = async { 42 }.await;
assert_eq!(result, 42);
}
测试异步流
use tokio_stream::StreamExt;
#[tokio::test]
async fn test_stream() {
let stream = tokio_stream::iter(vec![1, 2, 3, 4, 5]);
let sum: i32 = stream.fold(0, |acc, x| async move { acc + x }).await;
assert_eq!(sum, 15);
}
测试组织与模式
Setup 和 Teardown
#[cfg(test)]
mod tests {
use std::sync::Once;
static INIT: Once = Once::new();
// 全局初始化(只执行一次)
fn setup() {
INIT.call_once(|| {
// 初始化日志、数据库等
println!("Global setup");
});
}
// 使用 Drop 实现 teardown
struct TestGuard {
name: String,
}
impl TestGuard {
fn new(name: &str) -> Self {
println!("Setup: {}", name);
TestGuard { name: name.to_string() }
}
}
impl Drop for TestGuard {
fn drop(&mut self) {
println!("Teardown: {}", self.name);
}
}
#[test]
fn test_with_guard() {
setup();
let _guard = TestGuard::new("test_with_guard");
// 测试逻辑
assert!(true);
// _guard 在这里自动 drop,执行清理
}
}
Test Fixtures
#[cfg(test)]
mod tests {
struct DatabaseFixture {
connection: String,
}
impl DatabaseFixture {
fn new() -> Self {
// 创建测试数据库连接
DatabaseFixture {
connection: "test_db".to_string(),
}
}
fn insert(&self, data: &str) {
println!("Inserting: {} into {}", data, self.connection);
}
fn query(&self, id: i32) -> Option<String> {
Some(format!("data_{}", id))
}
}
impl Drop for DatabaseFixture {
fn drop(&mut self) {
// 清理测试数据
println!("Cleaning up {}", self.connection);
}
}
#[test]
fn test_database_insert() {
let db = DatabaseFixture::new();
db.insert("test_data");
let result = db.query(1);
assert!(result.is_some());
}
}
Builder 模式测试数据
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
email: String,
age: u8,
active: bool,
}
#[cfg(test)]
mod tests {
use super::*;
// 测试数据构建器
struct UserBuilder {
id: u64,
name: String,
email: String,
age: u8,
active: bool,
}
impl UserBuilder {
fn new() -> Self {
UserBuilder {
id: 1,
name: "Test User".to_string(),
email: "test@example.com".to_string(),
age: 25,
active: true,
}
}
fn id(mut self, id: u64) -> Self {
self.id = id;
self
}
fn name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
fn inactive(mut self) -> Self {
self.active = false;
self
}
fn build(self) -> User {
User {
id: self.id,
name: self.name,
email: self.email,
age: self.age,
active: self.active,
}
}
}
#[test]
fn test_user_creation() {
let user = UserBuilder::new()
.id(42)
.name("Alice")
.build();
assert_eq!(user.id, 42);
assert_eq!(user.name, "Alice");
assert!(user.active); // 使用默认值
}
}
Mocking 和依赖注入
Trait 抽象和手动 Mock
// 定义 trait 接口
trait Database {
fn get(&self, key: &str) -> Option<String>;
fn set(&mut self, key: &str, value: &str);
}
// 实际实现
struct RealDatabase {
// ...
}
impl Database for RealDatabase {
fn get(&self, key: &str) -> Option<String> {
// 真实数据库操作
None
}
fn set(&mut self, key: &str, value: &str) {
// 真实数据库操作
}
}
// 使用 trait 的业务逻辑
struct UserService<D: Database> {
db: D,
}
impl<D: Database> UserService<D> {
fn new(db: D) -> Self {
UserService { db }
}
fn get_user(&self, id: &str) -> Option<String> {
self.db.get(&format!("user:{}", id))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
// Mock 实现
struct MockDatabase {
data: HashMap<String, String>,
}
impl MockDatabase {
fn new() -> Self {
MockDatabase { data: HashMap::new() }
}
fn with_data(data: HashMap<String, String>) -> Self {
MockDatabase { data }
}
}
impl Database for MockDatabase {
fn get(&self, key: &str) -> Option<String> {
self.data.get(key).cloned()
}
fn set(&mut self, key: &str, value: &str) {
self.data.insert(key.to_string(), value.to_string());
}
}
#[test]
fn test_get_user() {
let mut data = HashMap::new();
data.insert("user:1".to_string(), "Alice".to_string());
let mock_db = MockDatabase::with_data(data);
let service = UserService::new(mock_db);
assert_eq!(service.get_user("1"), Some("Alice".to_string()));
assert_eq!(service.get_user("2"), None);
}
}
使用 mockall crate
[dev-dependencies]
mockall = "0.12"
use mockall::{automock, predicate::*};
#[automock]
trait HttpClient {
fn get(&self, url: &str) -> Result<String, String>;
fn post(&self, url: &str, body: &str) -> Result<String, String>;
}
struct ApiService<C: HttpClient> {
client: C,
}
impl<C: HttpClient> ApiService<C> {
fn fetch_user(&self, id: u64) -> Result<String, String> {
self.client.get(&format!("/users/{}", id))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fetch_user() {
let mut mock = MockHttpClient::new();
// 设置期望
mock.expect_get()
.with(eq("/users/42"))
.times(1)
.returning(|_| Ok(r#"{"name": "Alice"}"#.to_string()));
let service = ApiService { client: mock };
let result = service.fetch_user(42);
assert!(result.is_ok());
assert!(result.unwrap().contains("Alice"));
}
#[test]
fn test_fetch_user_error() {
let mut mock = MockHttpClient::new();
mock.expect_get()
.returning(|_| Err("Network error".to_string()));
let service = ApiService { client: mock };
let result = service.fetch_user(1);
assert!(result.is_err());
}
}
使用 mock_derive
use mockall::mock;
// 为现有结构体创建 mock
mock! {
pub Database {
fn connect(&self) -> bool;
fn query(&self, sql: &str) -> Vec<String>;
}
}
#[test]
fn test_with_mock_database() {
let mut mock = MockDatabase::new();
mock.expect_connect()
.returning(|| true);
mock.expect_query()
.with(mockall::predicate::str::starts_with("SELECT"))
.returning(|_| vec!["row1".to_string(), "row2".to_string()]);
assert!(mock.connect());
assert_eq!(mock.query("SELECT * FROM users").len(), 2);
}
参数化测试
使用 test-case crate
[dev-dependencies]
test-case = "3"
use test_case::test_case;
fn add(a: i32, b: i32) -> i32 {
a + b
}
#[test_case(1, 1, 2; "one plus one")]
#[test_case(2, 2, 4; "two plus two")]
#[test_case(-1, 1, 0; "negative plus positive")]
#[test_case(0, 0, 0; "zeros")]
fn test_add(a: i32, b: i32, expected: i32) {
assert_eq!(add(a, b), expected);
}
// 更复杂的示例
#[test_case("hello", 5)]
#[test_case("", 0)]
#[test_case("rust", 4)]
fn test_string_length(input: &str, expected: usize) {
assert_eq!(input.len(), expected);
}
// 使用 => 指定期望结果
#[test_case(4 => 2; "sqrt of 4")]
#[test_case(9 => 3; "sqrt of 9")]
fn test_sqrt(input: i32) -> i32 {
(input as f64).sqrt() as i32
}
// 期望 panic
#[test_case(0 => panics "divide by zero")]
fn test_divide_panic(divisor: i32) {
let _ = 100 / divisor;
}
// 使用 matches!
#[test_case("42" => matches Ok(_))]
#[test_case("hello" => matches Err(_))]
fn test_parse(input: &str) -> Result<i32, std::num::ParseIntError> {
input.parse()
}
使用 rstest crate
[dev-dependencies]
rstest = "0.18"
use rstest::rstest;
// 参数化测试
#[rstest]
#[case(1, 1, 2)]
#[case(2, 2, 4)]
#[case(0, 0, 0)]
fn test_add(#[case] a: i32, #[case] b: i32, #[case] expected: i32) {
assert_eq!(a + b, expected);
}
// Fixtures
#[rstest]
fn test_with_fixture(#[values(1, 2, 3)] x: i32) {
assert!(x > 0);
}
// 组合测试
#[rstest]
fn test_combinations(
#[values(1, 2)] a: i32,
#[values("x", "y")] b: &str
) {
// 运行 2 * 2 = 4 个测试用例
println!("a={}, b={}", a, b);
}
// Fixture 函数
#[rstest::fixture]
fn database() -> MockDatabase {
MockDatabase::new()
}
#[rstest]
fn test_with_db_fixture(database: MockDatabase) {
// 使用 fixture
assert!(database.is_connected());
}
// 异步测试
#[rstest]
#[tokio::test]
async fn test_async_with_params(#[values(1, 2, 3)] x: i32) {
let result = async { x * 2 }.await;
assert_eq!(result, x * 2);
}
手动参数化
#[cfg(test)]
mod tests {
struct TestCase {
input: i32,
expected: i32,
}
#[test]
fn test_double() {
let cases = vec![
TestCase { input: 1, expected: 2 },
TestCase { input: 2, expected: 4 },
TestCase { input: -1, expected: -2 },
TestCase { input: 0, expected: 0 },
];
for (i, case) in cases.iter().enumerate() {
let result = case.input * 2;
assert_eq!(
result, case.expected,
"Case {} failed: {} * 2 = {}, expected {}",
i, case.input, result, case.expected
);
}
}
}
属性测试 (Property-Based Testing)
使用 proptest
[dev-dependencies]
proptest = "1"
use proptest::prelude::*;
// 基本属性测试
proptest! {
#[test]
fn test_add_commutative(a: i32, b: i32) {
// 加法交换律
prop_assert_eq!(a.wrapping_add(b), b.wrapping_add(a));
}
#[test]
fn test_string_reverse_reverse(s: String) {
// 反转两次等于原始
let reversed: String = s.chars().rev().collect();
let double_reversed: String = reversed.chars().rev().collect();
prop_assert_eq!(s, double_reversed);
}
}
// 自定义生成策略
proptest! {
#[test]
fn test_vec_sort(mut v: Vec<i32>) {
v.sort();
// 排序后相邻元素有序
for window in v.windows(2) {
prop_assert!(window[0] <= window[1]);
}
}
// 限制输入范围
#[test]
fn test_percentage(n in 0..=100i32) {
prop_assert!(n >= 0 && n <= 100);
}
// 自定义字符串策略
#[test]
fn test_email_like(
local in "[a-z]{1,10}",
domain in "[a-z]{1,10}"
) {
let email = format!("{}@{}.com", local, domain);
prop_assert!(email.contains('@'));
}
}
// 复杂策略
fn user_strategy() -> impl Strategy<Value = User> {
(
"[a-zA-Z]{1,20}", // name
"[a-z]+@[a-z]+\\.[a-z]+", // email
18..100u8, // age
).prop_map(|(name, email, age)| User { name, email, age })
}
proptest! {
#[test]
fn test_user_serialization(user in user_strategy()) {
let json = serde_json::to_string(&user).unwrap();
let deserialized: User = serde_json::from_str(&json).unwrap();
prop_assert_eq!(user, deserialized);
}
}
使用 quickcheck
[dev-dependencies]
quickcheck = "1"
quickcheck_macros = "1"
use quickcheck_macros::quickcheck;
#[quickcheck]
fn test_reverse_reverse(xs: Vec<i32>) -> bool {
let reversed: Vec<i32> = xs.iter().rev().cloned().collect();
let double_reversed: Vec<i32> = reversed.iter().rev().cloned().collect();
xs == double_reversed
}
#[quickcheck]
fn test_sort_length(mut xs: Vec<i32>) -> bool {
let original_len = xs.len();
xs.sort();
xs.len() == original_len
}
模糊测试 (Fuzzing)
使用 cargo-fuzz
# 安装
cargo install cargo-fuzz
# 初始化
cargo fuzz init
# 创建 fuzz target
cargo fuzz add my_target
// fuzz/fuzz_targets/my_target.rs
#![no_main]
use libfuzzer_sys::fuzz_target;
use my_project::parse_input;
fuzz_target!(|data: &[u8]| {
if let Ok(s) = std::str::from_utf8(data) {
let _ = parse_input(s);
}
});
# 运行 fuzzing
cargo +nightly fuzz run my_target
# 限制时间
cargo +nightly fuzz run my_target -- -max_total_time=60
使用 arbitrary
[dev-dependencies]
arbitrary = { version = "1", features = ["derive"] }
use arbitrary::{Arbitrary, Unstructured};
#[derive(Debug, Arbitrary)]
struct MyInput {
name: String,
age: u8,
scores: Vec<i32>,
}
// 在 fuzz target 中使用
fuzz_target!(|input: MyInput| {
process(input);
});
基准测试
内置基准测试(nightly)
#![feature(test)]
extern crate test;
use test::Bencher;
#[bench]
fn bench_add(b: &mut Bencher) {
b.iter(|| {
// 使用 black_box 防止优化
test::black_box(add(1, 2))
});
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
使用 criterion
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "my_benchmark"
harness = false
// benches/my_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 1,
1 => 1,
n => fibonacci(n - 1) + fibonacci(n - 2),
}
}
// 基本基准测试
fn bench_fibonacci(c: &mut Criterion) {
c.bench_function("fib 20", |b| {
b.iter(|| fibonacci(black_box(20)))
});
}
// 参数化基准测试
fn bench_fibonacci_group(c: &mut Criterion) {
let mut group = c.benchmark_group("Fibonacci");
for i in [10, 15, 20].iter() {
group.bench_with_input(BenchmarkId::new("recursive", i), i, |b, i| {
b.iter(|| fibonacci(black_box(*i)))
});
}
group.finish();
}
// 比较不同实现
fn fibonacci_iterative(n: u64) -> u64 {
let mut a = 1;
let mut b = 1;
for _ in 0..n {
let tmp = a;
a = b;
b = tmp + b;
}
a
}
fn bench_compare(c: &mut Criterion) {
let mut group = c.benchmark_group("Fibonacci Comparison");
let n = 20;
group.bench_function("recursive", |b| {
b.iter(|| fibonacci(black_box(n)))
});
group.bench_function("iterative", |b| {
b.iter(|| fibonacci_iterative(black_box(n)))
});
group.finish();
}
// 吞吐量测试
fn bench_throughput(c: &mut Criterion) {
let data: Vec<u8> = (0..1024).map(|i| i as u8).collect();
let mut group = c.benchmark_group("Throughput");
group.throughput(criterion::Throughput::Bytes(data.len() as u64));
group.bench_function("process", |b| {
b.iter(|| {
// 处理数据
data.iter().sum::<u8>()
})
});
group.finish();
}
criterion_group!(
benches,
bench_fibonacci,
bench_fibonacci_group,
bench_compare,
bench_throughput
);
criterion_main!(benches);
# 运行基准测试
cargo bench
# 运行特定基准
cargo bench -- fibonacci
# 保存基准结果
cargo bench -- --save-baseline main
# 与基准比较
cargo bench -- --baseline main
代码覆盖率
使用 cargo-tarpaulin
# 安装
cargo install cargo-tarpaulin
# 运行覆盖率
cargo tarpaulin
# 生成 HTML 报告
cargo tarpaulin --out Html
# 忽略测试代码
cargo tarpaulin --ignore-tests
# 指定输出格式
cargo tarpaulin --out Xml --out Html
# 设置覆盖率阈值
cargo tarpaulin --fail-under 80
使用 llvm-cov
# 安装
cargo install cargo-llvm-cov
# 运行覆盖率
cargo llvm-cov
# 生成 HTML 报告
cargo llvm-cov --html
# 打开报告
cargo llvm-cov --open
# 只显示摘要
cargo llvm-cov --summary-only
CI 配置示例
# .github/workflows/coverage.yml
name: Coverage
on: [push, pull_request]
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install tarpaulin
run: cargo install cargo-tarpaulin
- name: Run coverage
run: cargo tarpaulin --out Xml
- name: Upload to codecov
uses: codecov/codecov-action@v3
with:
file: cobertura.xml
CI/CD 集成
GitHub Actions 完整配置
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
jobs:
# 格式检查
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
# Lint 检查
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- run: cargo clippy --all-targets --all-features -- -D warnings
# 测试
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
rust: [stable, beta]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- run: cargo test --all-features --verbose
# 文档测试
doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo doc --no-deps --all-features
env:
RUSTDOCFLAGS: -D warnings
# 覆盖率
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- run: cargo llvm-cov --all-features --lcov --output-path lcov.info
- uses: codecov/codecov-action@v3
with:
files: lcov.info
fail_ci_if_error: true
最佳实践
测试命名规范
#[cfg(test)]
mod tests {
// 格式:test_<功能>_<场景>_<期望结果>
#[test]
fn test_add_positive_numbers_returns_sum() {}
#[test]
fn test_add_negative_numbers_returns_sum() {}
#[test]
fn test_divide_by_zero_returns_none() {}
#[test]
fn test_parse_valid_json_returns_ok() {}
#[test]
fn test_parse_invalid_json_returns_error() {}
}
测试组织
#[cfg(test)]
mod tests {
use super::*;
// 按功能分组
mod add {
use super::*;
#[test]
fn positive_numbers() {}
#[test]
fn negative_numbers() {}
}
mod subtract {
use super::*;
#[test]
fn positive_numbers() {}
}
// 辅助函数
fn create_test_user() -> User {
User::new("test", "test@example.com")
}
}
测试隔离
#[cfg(test)]
mod tests {
use std::sync::Mutex;
use once_cell::sync::Lazy;
// 需要串行执行的测试使用锁
static TEST_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
#[test]
fn test_that_modifies_global_state() {
let _lock = TEST_MUTEX.lock().unwrap();
// 测试逻辑
}
// 或者使用 serial_test crate
// #[serial]
// fn test_serial() {}
}
错误信息
#[test]
fn test_with_good_error_message() {
let input = vec![1, 2, 3];
let result = process(&input);
assert!(
result.len() == 3,
"Expected result length 3, got {}. Input: {:?}, Result: {:?}",
result.len(),
input,
result
);
}
速查表
常用命令
cargo test # 运行所有测试
cargo test test_name # 运行匹配名称的测试
cargo test -- --nocapture # 显示 println! 输出
cargo test -- --test-threads=1 # 单线程运行
cargo test -- --ignored # 运行忽略的测试
cargo test --doc # 只运行文档测试
cargo test --lib # 只运行单元测试
cargo test --test name # 运行特定集成测试
cargo bench # 运行基准测试
测试属性
| 属性 | 用途 |
|---|---|
#[test] |
标记测试函数 |
#[should_panic] |
期望 panic |
#[should_panic(expected = "msg")] |
期望特定 panic 消息 |
#[ignore] |
跳过测试 |
#[ignore = "reason"] |
带原因跳过 |
#[cfg(test)] |
仅测试时编译 |
断言宏
| 宏 | 用途 |
|---|---|
assert!(expr) |
断言为真 |
assert_eq!(a, b) |
断言相等 |
assert_ne!(a, b) |
断言不相等 |
debug_assert! |
Debug 模式断言 |
panic!("msg") |
触发 panic |
常用测试 Crate
| Crate | 用途 |
|---|---|
mockall |
Mock 对象 |
test-case |
参数化测试 |
rstest |
参数化 + Fixtures |
proptest |
属性测试 |
criterion |
基准测试 |
fake |
假数据生成 |
wiremock |
HTTP Mock |
tempfile |
临时文件 |
serial_test |
串行测试 |
Cargo
概览
┌─────────────────────────────────────────────────────────────────────┐
│ Cargo 功能 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 包管理器 │ │ 构建系统 │ │ 任务运行器 │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ • 依赖解析 │ │ • 编译代码 │ │ • 运行测试 │ │
│ │ • 版本管理 │ │ • 增量构建 │ │ • 运行示例 │ │
│ │ • 发布包 │ │ • 多目标 │ │ • 基准测试 │ │
│ │ • 下载依赖 │ │ • 构建脚本 │ │ • 文档生成 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ 项目结构 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ my_project/ │
│ ├── Cargo.toml # 项目配置文件 │
│ ├── Cargo.lock # 依赖锁定文件(自动生成) │
│ ├── src/ │
│ │ ├── lib.rs # 库入口 │
│ │ ├── main.rs # 二进制入口 │
│ │ └── bin/ # 多个二进制 │
│ │ └── other.rs │
│ ├── tests/ # 集成测试 │
│ ├── benches/ # 基准测试 │
│ ├── examples/ # 示例代码 │
│ └── build.rs # 构建脚本(可选) │
│ │
└─────────────────────────────────────────────────────────────────────┘
基础命令
项目管理
# ============ 创建项目 ============
cargo new my_project # 创建二进制项目
cargo new my_lib --lib # 创建库项目
cargo new my_project --vcs none # 不初始化 git
cargo init # 在当前目录初始化
cargo init --lib # 初始化为库
# ============ 构建 ============
cargo build # Debug 构建
cargo build --release # Release 构建
cargo build --target x86_64-unknown-linux-gnu # 交叉编译
# ============ 运行 ============
cargo run # 构建并运行
cargo run --release # Release 模式运行
cargo run --bin my_bin # 运行指定二进制
cargo run --example my_example # 运行示例
cargo run -- arg1 arg2 # 传递命令行参数
# ============ 检查(快速) ============
cargo check # 只检查,不生成代码
cargo check --all-targets # 检查所有目标
测试和文档
# ============ 测试 ============
cargo test # 运行所有测试
cargo test test_name # 运行匹配的测试
cargo test --lib # 只测试库
cargo test --doc # 只运行文档测试
cargo test --test integration # 运行特定集成测试
cargo test -- --nocapture # 显示 println 输出
cargo test -- --test-threads=1 # 单线程
# ============ 文档 ============
cargo doc # 生成文档
cargo doc --open # 生成并打开
cargo doc --no-deps # 不包含依赖的文档
cargo doc --document-private-items # 包含私有项
# ============ 基准测试 ============
cargo bench # 运行基准测试(nightly)
cargo bench --bench my_bench # 运行特定基准
依赖管理
# ============ 依赖操作 ============
cargo add serde # 添加依赖
cargo add serde --features derive # 添加带 feature
cargo add tokio@1.0 # 指定版本
cargo add --dev pretty_assertions # 添加开发依赖
cargo add --build cc # 添加构建依赖
cargo remove serde # 移除依赖
cargo update # 更新所有依赖
cargo update -p serde # 更新特定依赖
# ============ 依赖信息 ============
cargo tree # 依赖树
cargo tree -d # 只显示重复依赖
cargo tree -i serde # 显示谁依赖 serde
cargo tree --format "{p} {f}" # 自定义格式
cargo search serde # 搜索 crate
cargo info serde # 查看 crate 信息
发布
# ============ 发布准备 ============
cargo login # 登录 crates.io
cargo package # 打包(检查)
cargo package --list # 列出将包含的文件
# ============ 发布 ============
cargo publish # 发布到 crates.io
cargo publish --dry-run # 模拟发布
cargo publish --allow-dirty # 允许未提交的更改
# ============ 安装 ============
cargo install ripgrep # 安装二进制
cargo install --path . # 从本地安装
cargo install --git https://github.com/user/repo
cargo uninstall ripgrep # 卸载
其他实用命令
# ============ 清理 ============
cargo clean # 清理 target 目录
cargo clean -p my_crate # 清理特定 crate
# ============ 代码质量 ============
cargo fmt # 格式化代码
cargo fmt -- --check # 检查格式
cargo clippy # 运行 linter
cargo clippy -- -D warnings # 警告视为错误
# ============ 审计 ============
cargo audit # 安全漏洞检查(需安装)
cargo outdated # 检查过期依赖(需安装)
# ============ 其他 ============
cargo locate-project # 显示 Cargo.toml 路径
cargo metadata # 输出项目元数据(JSON)
cargo version # 显示版本
cargo --list # 列出所有子命令
Cargo.toml 详解
基本结构
# ============ 包信息 ============
[package]
name = "my_project"
version = "0.1.0"
edition = "2021" # 2015, 2018, 2021, 2024
rust-version = "1.70" # 最低 Rust 版本
# 元数据
authors = ["Your Name <email@example.com>"]
description = "A short description"
documentation = "https://docs.rs/my_project"
readme = "README.md"
homepage = "https://example.com"
repository = "https://github.com/user/my_project"
license = "MIT OR Apache-2.0"
license-file = "LICENSE"
keywords = ["keyword1", "keyword2"] # 最多 5 个
categories = ["development-tools"] # 见 crates.io/category_slugs
exclude = ["tests/*", "benches/*"] # 发布时排除
include = ["src/**/*", "Cargo.toml"] # 发布时包含(优先于 exclude)
# 构建配置
build = "build.rs" # 构建脚本
links = "foo" # 链接的原生库名
autobins = true # 自动发现二进制
autoexamples = true # 自动发现示例
autotests = true # 自动发现测试
autobenches = true # 自动发现基准
# 解析器版本
resolver = "2" # 推荐使用 2
# ============ 库配置 ============
[lib]
name = "my_lib" # 库名(默认为包名)
path = "src/lib.rs" # 入口路径
crate-type = ["lib"] # lib, rlib, dylib, cdylib, staticlib, proc-macro
doc = true # 是否生成文档
test = true # 是否测试
doctest = true # 是否运行文档测试
bench = true # 是否运行基准
# ============ 二进制配置 ============
[[bin]]
name = "my_bin"
path = "src/main.rs"
test = true
bench = true
doc = true
[[bin]]
name = "another_bin"
path = "src/bin/another.rs"
# ============ 示例 ============
[[example]]
name = "my_example"
path = "examples/my_example.rs"
crate-type = ["bin"] # 默认 bin
required-features = ["feature1"] # 需要的 feature
# ============ 测试 ============
[[test]]
name = "integration_test"
path = "tests/integration.rs"
harness = true # 使用 libtest harness
# ============ 基准测试 ============
[[bench]]
name = "my_bench"
path = "benches/my_bench.rs"
harness = false # 使用自定义 harness(如 criterion)
依赖配置
[dependencies]
# 简单版本
serde = "1.0"
# 带 features
serde = { version = "1.0", features = ["derive"] }
# 可选依赖
serde_json = { version = "1.0", optional = true }
# Git 依赖
my_crate = { git = "https://github.com/user/repo" }
my_crate = { git = "https://github.com/user/repo", branch = "main" }
my_crate = { git = "https://github.com/user/repo", tag = "v1.0" }
my_crate = { git = "https://github.com/user/repo", rev = "abc123" }
# 本地路径
my_crate = { path = "../my_crate" }
# 路径 + 版本(发布时使用版本)
my_crate = { path = "../my_crate", version = "1.0" }
# 重命名
serde_json_renamed = { package = "serde_json", version = "1.0" }
# 禁用默认 features
tokio = { version = "1", default-features = false, features = ["rt"] }
# 平台特定依赖
[target.'cfg(windows)'.dependencies]
winapi = "0.3"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
# 开发依赖
[dev-dependencies]
pretty_assertions = "1.0"
mockall = "0.11"
criterion = { version = "0.5", features = ["html_reports"] }
# 构建依赖
[build-dependencies]
cc = "1.0"
bindgen = "0.69"
版本规范
[dependencies]
# 语义化版本
exact = "=1.2.3" # 精确版本
caret = "^1.2.3" # 默认,>=1.2.3 <2.0.0
tilde = "~1.2.3" # >=1.2.3 <1.3.0
wildcard = "1.*" # >=1.0.0 <2.0.0
range = ">=1.2, <1.5" # 范围
# 常见模式
latest_1x = "1" # >=1.0.0 <2.0.0
patch_only = "~1.2" # >=1.2.0 <1.3.0
Features 配置
[features]
# 默认启用的 features
default = ["std", "derive"]
# 定义 features
std = []
derive = ["serde/derive"] # 启用依赖的 feature
full = ["std", "derive", "async"]
async = ["tokio", "async-trait"]
# 可选依赖作为 feature
serde = ["dep:serde"] # 使用 dep: 前缀避免歧义
json = ["dep:serde_json", "serde"] # 依赖其他 feature
[dependencies]
serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
tokio = { version = "1", optional = true }
async-trait = { version = "0.1", optional = true }
Profiles 配置
# ============ 内置配置 ============
[profile.dev]
opt-level = 0 # 优化级别 0-3, "s", "z"
debug = true # 调试信息
split-debuginfo = "..." # 调试信息分离
debug-assertions = true # 调试断言
overflow-checks = true # 溢出检查
lto = false # 链接时优化: false, true/"fat", "thin"
panic = "unwind" # panic 策略: "unwind", "abort"
incremental = true # 增量编译
codegen-units = 256 # 代码生成单元
rpath = false # rpath
[profile.release]
opt-level = 3
debug = false
debug-assertions = false
overflow-checks = false
lto = true
panic = "abort"
incremental = false
codegen-units = 1
strip = true # 去除符号: true, "debuginfo", "symbols"
# ============ 测试/基准配置 ============
[profile.test]
opt-level = 0
debug = 2
[profile.bench]
opt-level = 3
debug = false
# ============ 自定义配置 ============
[profile.release-with-debug]
inherits = "release"
debug = true
# 使用: cargo build --profile release-with-debug
# ============ 依赖特定配置 ============
[profile.dev.package."*"] # 所有依赖
opt-level = 2 # 依赖用 O2 优化
[profile.dev.package.serde]
opt-level = 3 # serde 用 O3
# 构建脚本配置
[profile.dev.build-override]
opt-level = 3
工作空间 (Workspace)
基本结构
my_workspace/
├── Cargo.toml # 工作空间配置
├── crates/
│ ├── core/
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ ├── utils/
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ └── app/
│ ├── Cargo.toml
│ └── src/main.rs
└── shared/
├── Cargo.toml
└── src/lib.rs
工作空间配置
# 根目录 Cargo.toml
[workspace]
# 成员列表
members = [
"crates/*", # 通配符
"shared",
]
# 排除
exclude = [
"crates/experimental",
]
# 默认成员(cargo build 时)
default-members = [
"crates/app",
]
# 解析器版本
resolver = "2"
# ============ 共享配置 ============
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Your Name <email@example.com>"]
license = "MIT"
repository = "https://github.com/user/repo"
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
thiserror = "1.0"
# 内部 crate(便于版本管理)
my_core = { path = "crates/core", version = "0.1.0" }
my_utils = { path = "crates/utils", version = "0.1.0" }
成员 crate 配置
# crates/core/Cargo.toml
[package]
name = "my_core"
version.workspace = true # 继承工作空间版本
edition.workspace = true
authors.workspace = true
license.workspace = true
[dependencies]
serde.workspace = true # 继承工作空间依赖
tokio = { workspace = true, features = ["rt"] } # 可覆盖 features
# crates/app/Cargo.toml
[package]
name = "my_app"
version.workspace = true
edition.workspace = true
[dependencies]
my_core.workspace = true # 使用内部 crate
my_utils = { workspace = true, optional = true }
工作空间命令
# 构建所有成员
cargo build --workspace
# 构建特定成员
cargo build -p my_core
cargo build -p my_app
# 测试所有成员
cargo test --workspace
# 运行特定二进制
cargo run -p my_app
# 发布(按依赖顺序)
cargo publish -p my_core
cargo publish -p my_utils
cargo publish -p my_app
构建脚本 (build.rs)
基本结构
// build.rs
fn main() {
// 构建脚本代码
// 告诉 Cargo 何时重新运行
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/wrapper.h");
// 传递给 rustc 的配置
println!("cargo:rustc-cfg=feature=\"special\"");
// 链接库
println!("cargo:rustc-link-lib=mylib");
println!("cargo:rustc-link-search=native=/usr/local/lib");
// 传递给依赖此 crate 的构建脚本
println!("cargo:root=/path/to/root");
// 警告信息
println!("cargo:warning=Something to note");
}
常用指令
fn main() {
// ============ 重新运行条件 ============
println!("cargo:rerun-if-changed=PATH"); // 文件变化时
println!("cargo:rerun-if-env-changed=VAR"); // 环境变量变化时
// ============ Rustc 配置 ============
println!("cargo:rustc-link-lib=static=foo"); // 静态链接
println!("cargo:rustc-link-lib=dylib=bar"); // 动态链接
println!("cargo:rustc-link-lib=framework=CoreFoundation"); // macOS
println!("cargo:rustc-link-search=native=/path");
println!("cargo:rustc-link-search=framework=/path");
println!("cargo:rustc-link-arg=-fuse-ld=lld"); // 链接参数
println!("cargo:rustc-link-arg-bins=-Wl,-rpath,/path"); // 仅二进制
println!("cargo:rustc-flags=-l foo -L /path");
println!("cargo:rustc-cfg=my_cfg");
println!("cargo:rustc-cfg=feature=\"foo\"");
println!("cargo:rustc-env=VAR=value");
println!("cargo:rustc-cdylib-link-arg=-Wl,--version-script=exports.txt");
// ============ 元数据 ============
println!("cargo:KEY=VALUE"); // 传递给依赖者
}
实用示例
// build.rs - 编译 C 代码
fn main() {
cc::Build::new()
.file("src/foo.c")
.file("src/bar.c")
.include("include")
.flag("-Wall")
.compile("mylib");
println!("cargo:rerun-if-changed=src/foo.c");
println!("cargo:rerun-if-changed=src/bar.c");
}
// build.rs - 生成绑定
fn main() {
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
// 在代码中使用
// include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
// build.rs - 条件编译
fn main() {
let target = std::env::var("TARGET").unwrap();
if target.contains("windows") {
println!("cargo:rustc-cfg=windows_build");
}
if target.contains("linux") {
println!("cargo:rustc-link-lib=pthread");
}
// 检查可用功能
let has_avx2 = std::env::var("CARGO_CFG_TARGET_FEATURE")
.map(|f| f.contains("avx2"))
.unwrap_or(false);
if has_avx2 {
println!("cargo:rustc-cfg=has_avx2");
}
}
// build.rs - 生成代码
use std::env;
use std::fs;
use std::path::Path;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("generated.rs");
let code = r#"
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const BUILD_TIME: &str = "2024-01-01";
pub fn generated_function() -> i32 {
42
}
"#;
fs::write(&dest_path, code).unwrap();
println!("cargo:rerun-if-changed=build.rs");
}
// 在代码中使用
// include!(concat!(env!("OUT_DIR"), "/generated.rs"));
构建脚本环境变量
fn main() {
// Cargo 提供的环境变量
let pkg_name = env!("CARGO_PKG_NAME");
let pkg_version = env!("CARGO_PKG_VERSION");
let manifest_dir = env!("CARGO_MANIFEST_DIR");
// 运行时获取
let out_dir = std::env::var("OUT_DIR").unwrap();
let target = std::env::var("TARGET").unwrap();
let host = std::env::var("HOST").unwrap();
let profile = std::env::var("PROFILE").unwrap(); // debug/release
let opt_level = std::env::var("OPT_LEVEL").unwrap();
let num_jobs = std::env::var("NUM_JOBS").unwrap();
// 目标配置
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
// features
// CARGO_FEATURE_<name> 存在表示启用了该 feature
let has_serde = std::env::var("CARGO_FEATURE_SERDE").is_ok();
}
配置文件
.cargo/config.toml
# .cargo/config.toml(项目级或全局 ~/.cargo/config.toml)
# ============ 构建配置 ============
[build]
jobs = 8 # 并行任务数
rustc-wrapper = "sccache" # 编译缓存
target = "x86_64-unknown-linux-gnu" # 默认目标
target-dir = "target" # 输出目录
incremental = true # 增量编译
rustflags = ["-C", "target-cpu=native"]
rustdocflags = ["--cfg", "docsrs"]
# ============ 目标配置 ============
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
runner = "my_runner" # cargo run 使用的运行器
[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F411CEUx"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "target-feature=+crt-static"]
# ============ 别名 ============
[alias]
b = "build"
c = "check"
t = "test"
r = "run"
rr = "run --release"
br = "build --release"
# 复杂命令
xtask = "run --package xtask --"
coverage = "llvm-cov --html"
ci = ["fmt -- --check", "clippy", "test"]
# ============ 注册表 ============
[registries]
my-registry = { index = "https://my-registry.example.com/git/index" }
[registry]
default = "crates-io" # 默认注册表
[source.crates-io]
replace-with = "ustc" # 镜像替换
[source.ustc]
registry = "https://mirrors.ustc.edu.cn/crates.io-index"
# Git 替换
[source.my-git]
git = "https://github.com/example/repo"
# 本地替换(开发调试用)
[source.crates-io.replace-with]
local-registry = "/path/to/local/registry"
# ============ 网络 ============
[net]
retry = 2 # 重试次数
git-fetch-with-cli = true # 使用 git 命令
offline = false # 离线模式
[http]
proxy = "http://proxy.example.com:8080"
timeout = 30
cainfo = "/path/to/cert.pem"
check-revoke = true
ssl-version = "tlsv1.3"
low-speed-limit = 5 # bytes/sec
multiplexing = true
# ============ 终端 ============
[term]
verbose = false # 详细输出
color = "auto" # always, never, auto
progress.when = "auto"
progress.width = 80
# ============ 环境变量 ============
[env]
MY_VAR = "value"
MY_PATH = { value = "/path", relative = true }
MY_FORCE = { value = "forced", force = true }
环境变量
# 常用环境变量
CARGO_HOME=~/.cargo # Cargo 主目录
CARGO_TARGET_DIR=./target # 构建输出目录
CARGO_BUILD_JOBS=8 # 并行任务数
CARGO_INCREMENTAL=1 # 增量编译
CARGO_PROFILE_RELEASE_LTO=true # Profile 选项
RUSTFLAGS="-C target-cpu=native" # Rustc 标志
RUSTDOCFLAGS="--cfg docsrs" # Rustdoc 标志
# 网络
CARGO_HTTP_TIMEOUT=30
CARGO_NET_RETRY=3
CARGO_NET_OFFLINE=true
# 测试
RUST_TEST_THREADS=1
RUST_BACKTRACE=1
# 日志
CARGO_LOG=debug
RUST_LOG=cargo=debug
Cargo 扩展工具
常用扩展
# ============ 安装 ============
# 代码质量
cargo install cargo-edit # cargo add/rm/upgrade
cargo install cargo-watch # 文件变化自动运行
cargo install cargo-audit # 安全审计
cargo install cargo-outdated # 检查过期依赖
cargo install cargo-deny # 依赖策略检查
# 分析
cargo install cargo-bloat # 分析二进制大小
cargo install cargo-expand # 展开宏
cargo install cargo-flamegraph # 火焰图
cargo install cargo-llvm-lines # LLVM IR 行数
# 测试
cargo install cargo-tarpaulin # 代码覆盖率
cargo install cargo-llvm-cov # 代码覆盖率
cargo install cargo-fuzz # 模糊测试
cargo install cargo-nextest # 更快的测试运行器
# 发布
cargo install cargo-release # 发布流程自动化
cargo install cargo-semver-checks # API 兼容性检查
# 嵌入式
cargo install cargo-binutils # 二进制工具
cargo install cargo-flash # 烧录
cargo install cargo-embed # 调试
# 其他
cargo install cargo-make # 任务运行器
cargo install cargo-generate # 项目模板
cargo install cargo-udeps # 检查未使用依赖
cargo-watch
# 文件变化时自动运行
cargo watch -x check # 自动检查
cargo watch -x test # 自动测试
cargo watch -x "run -- --arg" # 自动运行
cargo watch -x clippy -x test # 多命令
# 自定义监视
cargo watch -w src -w tests # 指定监视目录
cargo watch --ignore "*.txt" # 忽略文件
cargo watch -c # 运行前清屏
cargo watch -d 2 # 延迟 2 秒
cargo-make
# Makefile.toml
[env]
RUST_BACKTRACE = "1"
[tasks.format]
command = "cargo"
args = ["fmt", "--all"]
[tasks.lint]
command = "cargo"
args = ["clippy", "--all-targets", "--", "-D", "warnings"]
[tasks.test]
command = "cargo"
args = ["test", "--all-features"]
[tasks.build-release]
command = "cargo"
args = ["build", "--release"]
[tasks.ci]
dependencies = ["format", "lint", "test"]
[tasks.dev]
run_task = { name = ["format", "lint", "test"], parallel = true }
# 带条件的任务
[tasks.build-windows]
condition = { platforms = ["windows"] }
command = "cargo"
args = ["build", "--target", "x86_64-pc-windows-msvc"]
cargo make ci
cargo make dev
cargo make build-release
cargo-release
# Cargo.toml 或 release.toml
[package.metadata.release]
sign-commit = true
sign-tag = true
pre-release-commit-message = "Release {{version}}"
tag-message = "Release {{version}}"
tag-prefix = "v"
pre-release-replacements = [
{ file = "CHANGELOG.md", search = "Unreleased", replace = "{{version}}" },
]
cargo release patch # 0.1.0 -> 0.1.1
cargo release minor # 0.1.0 -> 0.2.0
cargo release major # 0.1.0 -> 1.0.0
cargo release --dry-run # 模拟运行
cargo release --execute # 实际执行
发布到 crates.io
准备工作
# Cargo.toml - 必需字段
[package]
name = "my_crate"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0" # 必需
description = "A short description" # 必需
repository = "https://github.com/user/repo" # 推荐
documentation = "https://docs.rs/my_crate"
readme = "README.md"
keywords = ["keyword1", "keyword2"]
categories = ["development-tools"]
# 排除不必要的文件
exclude = [
"tests/*",
"benches/*",
".github/*",
"*.log",
]
发布前检查
# 检查可发布性
cargo publish --dry-run
# 检查包内容
cargo package --list
# 本地安装测试
cargo install --path .
# API 兼容性(需要 cargo-semver-checks)
cargo semver-checks check-release
发布流程
# 1. 登录
cargo login <token>
# 2. 发布
cargo publish
# 3. Yanking(撤回版本,不能删除)
cargo yank --version 0.1.0
cargo yank --version 0.1.0 --undo # 恢复
文档发布
# docs.rs 自动从 crates.io 构建
# 配置 docs.rs 构建
# Cargo.toml
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-unknown-linux-gnu"]
// lib.rs
#![cfg_attr(docsrs, feature(doc_cfg))]
/// 仅在特定 feature 下显示
#[cfg(feature = "advanced")]
#[cfg_attr(docsrs, doc(cfg(feature = "advanced")))]
pub fn advanced_function() {}
常见模式
条件编译
# Cargo.toml
[features]
default = ["std"]
std = []
alloc = []
[dependencies]
serde = { version = "1", optional = true }
// lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
pub fn std_only() {}
#[cfg(feature = "serde")]
mod serde_impl;
多平台支持
# Cargo.toml
[target.'cfg(windows)'.dependencies]
winapi = "0.3"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
示例项目组织
# Cargo.toml
# 需要特定依赖的示例
[[example]]
name = "async_example"
required-features = ["tokio"]
[[example]]
name = "serde_example"
required-features = ["serde"]
[dev-dependencies]
tokio = { version = "1", features = ["full"] }
xtask 模式
my_project/
├── Cargo.toml
├── src/
├── xtask/
│ ├── Cargo.toml
│ └── src/main.rs
# 根 Cargo.toml
[workspace]
members = [".", "xtask"]
# .cargo/config.toml
[alias]
xtask = "run --package xtask --"
// xtask/src/main.rs
use std::process::Command;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
match args.first().map(|s| s.as_str()) {
Some("dist") => dist(),
Some("coverage") => coverage(),
Some("ci") => ci(),
_ => {
eprintln!("Usage: cargo xtask <task>");
eprintln!("Tasks: dist, coverage, ci");
}
}
}
fn dist() {
Command::new("cargo")
.args(["build", "--release"])
.status()
.expect("Failed to build");
// 打包逻辑
}
fn coverage() {
Command::new("cargo")
.args(["llvm-cov", "--html"])
.status()
.expect("Failed to run coverage");
}
fn ci() {
// 运行所有 CI 检查
}
cargo xtask dist
cargo xtask coverage
速查表
常用命令
| 命令 | 说明 |
|---|---|
cargo new/init |
创建项目 |
cargo build |
构建 |
cargo run |
构建并运行 |
cargo check |
快速检查 |
cargo test |
运行测试 |
cargo bench |
运行基准测试 |
cargo doc |
生成文档 |
cargo publish |
发布 |
cargo add/remove |
管理依赖 |
cargo update |
更新依赖 |
cargo tree |
依赖树 |
cargo fmt |
格式化 |
cargo clippy |
Lint |
cargo clean |
清理 |
常用标志
| 标志 | 说明 |
|---|---|
--release |
Release 模式 |
--workspace |
所有工作空间成员 |
-p <name> |
指定包 |
--all-features |
启用所有 features |
--no-default-features |
禁用默认 features |
--features "a b" |
启用指定 features |
--target <triple> |
指定目标 |
--verbose/-v |
详细输出 |
--jobs/-j <N> |
并行任务数 |
环境变量
| 变量 | 说明 |
|---|---|
CARGO_HOME |
Cargo 主目录 |
CARGO_TARGET_DIR |
输出目录 |
RUSTFLAGS |
编译器标志 |
CARGO_INCREMENTAL |
增量编译 |
RUST_BACKTRACE |
错误回溯 |
文件
| 文件 | 说明 |
|---|---|
Cargo.toml |
项目配置 |
Cargo.lock |
依赖锁定 |
.cargo/config.toml |
Cargo 配置 |
build.rs |
构建脚本 |
rust-toolchain.toml |
工具链配置 |
no_std 编程
概念概览
┌─────────────────────────────────────────────────────────────────────┐
│ Rust 标准库层次 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ std │ │
│ │ • 依赖操作系统(文件、网络、线程、时间...) │ │
│ │ • 自动链接,提供运行时 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ alloc │ │
│ │ • 堆内存分配(Box, Vec, String, Rc, Arc...) │ │
│ │ • 需要全局分配器 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ core │ │
│ │ • 无任何依赖(Option, Result, Iterator, 切片...) │ │
│ │ • 纯 Rust,可在任何环境运行 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ no_std 应用场景 │
├─────────────────────────────────────────────────────────────────────┤
│ • 嵌入式系统 (ARM Cortex-M, RISC-V, AVR...) │
│ • 操作系统内核开发 │
│ • Bootloader │
│ • WebAssembly (部分场景) │
│ • 驱动程序 │
│ • 实时系统 │
└─────────────────────────────────────────────────────────────────────┘
std vs no_std 对比
| 特性 | std | no_std | no_std + alloc |
|---|---|---|---|
| 堆内存 | ✅ | ❌ | ✅ |
| Vec/String/Box | ✅ | ❌ | ✅ |
| 文件 I/O | ✅ | ❌ | ❌ |
| 网络 | ✅ | ❌ | ❌ |
| 线程 | ✅ | ❌ | ❌ |
| 时间 | ✅ | ❌ | ❌ |
| HashMap | ✅ | ❌ | ❌ |
| println! | ✅ | ❌ | ❌ |
| panic 信息 | ✅ | 需实现 | 需实现 |
基础 no_std 项目
最小示例
// src/lib.rs
#![no_std] // 不链接标准库
// 如果是二进制程序,还需要
// #![no_main] // 不使用标准入口点
// 必须定义 panic 处理
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
// 现在可以使用 core 库的内容
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn find_max(slice: &[i32]) -> Option<&i32> {
slice.iter().max()
}
# Cargo.toml
[package]
name = "my_no_std_lib"
version = "0.1.0"
edition = "2021"
[lib]
# 可选:生成静态库
# crate-type = ["staticlib"]
[profile.release]
panic = "abort" # 减小二进制大小
lto = true # 链接时优化
opt-level = "z" # 优化大小
core 库详解
可用类型和 trait
#![no_std]
use core::{
// 基础类型操作
mem,
ptr,
slice,
str,
// 常用 trait
cmp::{Ord, PartialOrd, Eq, PartialEq, Ordering},
convert::{From, Into, TryFrom, TryInto, AsRef, AsMut},
default::Default,
fmt::{Debug, Display, Formatter, Write},
hash::{Hash, Hasher},
iter::{Iterator, IntoIterator, FromIterator},
marker::{Copy, Send, Sync, Sized, PhantomData},
ops::{Add, Sub, Mul, Div, Index, Deref, Drop, Fn, FnMut, FnOnce},
// 错误处理
option::Option,
result::Result,
// 数值
num::Wrapping,
// 同步原语
sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering},
cell::{Cell, RefCell, UnsafeCell},
// 时间(无系统时钟)
time::Duration,
// 其他
any::Any,
borrow::{Borrow, BorrowMut},
clone::Clone,
hint,
};
// 示例:使用 core 实现功能
pub fn sum_slice(data: &[i32]) -> i32 {
data.iter().fold(0, |acc, x| acc.wrapping_add(*x))
}
pub fn find_index<T: PartialEq>(slice: &[T], target: &T) -> Option<usize> {
slice.iter().position(|x| x == target)
}
格式化输出(无 println!)
#![no_std]
use core::fmt::{self, Write};
// 自定义写入目标
pub struct Buffer {
data: [u8; 256],
pos: usize,
}
impl Buffer {
pub const fn new() -> Self {
Buffer {
data: [0; 256],
pos: 0,
}
}
pub fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.data[..self.pos]) }
}
pub fn clear(&mut self) {
self.pos = 0;
}
}
impl Write for Buffer {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let remaining = self.data.len() - self.pos;
if bytes.len() > remaining {
return Err(fmt::Error);
}
self.data[self.pos..self.pos + bytes.len()].copy_from_slice(bytes);
self.pos += bytes.len();
Ok(())
}
}
// 使用 write! 宏
pub fn format_number(buf: &mut Buffer, n: i32) -> fmt::Result {
write!(buf, "Number: {}", n)
}
// 实现 Debug/Display
pub struct Point {
x: i32,
y: i32,
}
impl fmt::Debug for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
原子操作
#![no_std]
use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
// 无锁计数器
pub struct Counter {
value: AtomicUsize,
}
impl Counter {
pub const fn new() -> Self {
Counter {
value: AtomicUsize::new(0),
}
}
pub fn increment(&self) -> usize {
self.value.fetch_add(1, Ordering::SeqCst)
}
pub fn get(&self) -> usize {
self.value.load(Ordering::SeqCst)
}
}
// 自旋锁
pub struct SpinLock {
locked: AtomicBool,
}
impl SpinLock {
pub const fn new() -> Self {
SpinLock {
locked: AtomicBool::new(false),
}
}
pub fn lock(&self) {
while self.locked.compare_exchange_weak(
false,
true,
Ordering::Acquire,
Ordering::Relaxed
).is_err() {
// 自旋等待
core::hint::spin_loop();
}
}
pub fn unlock(&self) {
self.locked.store(false, Ordering::Release);
}
pub fn try_lock(&self) -> bool {
self.locked
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}
}
// 使用示例
static COUNTER: Counter = Counter::new();
static LOCK: SpinLock = SpinLock::new();
alloc 库使用
启用 alloc
#![no_std]
extern crate alloc;
use alloc::{
boxed::Box,
vec::Vec,
vec,
string::String,
format,
rc::Rc,
sync::Arc,
collections::{BTreeMap, BTreeSet, VecDeque, LinkedList, BinaryHeap},
borrow::Cow,
};
// 使用堆分配类型
pub fn create_vector() -> Vec<i32> {
vec![1, 2, 3, 4, 5]
}
pub fn boxed_value() -> Box<[u8; 1024]> {
Box::new([0u8; 1024])
}
pub fn use_btree_map() -> BTreeMap<&'static str, i32> {
let mut map = BTreeMap::new();
map.insert("one", 1);
map.insert("two", 2);
map
}
全局分配器
#![no_std]
#![feature(alloc_error_handler)]
extern crate alloc;
use core::alloc::{GlobalAlloc, Layout};
// 简单的 bump 分配器
pub struct BumpAllocator {
heap_start: usize,
heap_end: usize,
next: core::sync::atomic::AtomicUsize,
}
impl BumpAllocator {
pub const fn new() -> Self {
BumpAllocator {
heap_start: 0,
heap_end: 0,
next: core::sync::atomic::AtomicUsize::new(0),
}
}
/// 初始化分配器
///
/// # Safety
/// 调用者必须确保给定的内存区域有效且未被使用
pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) {
self.heap_start = heap_start;
self.heap_end = heap_start + heap_size;
self.next.store(heap_start, core::sync::atomic::Ordering::SeqCst);
}
}
unsafe impl GlobalAlloc for BumpAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
use core::sync::atomic::Ordering;
loop {
let current = self.next.load(Ordering::Relaxed);
let aligned = align_up(current, layout.align());
let new_next = aligned + layout.size();
if new_next > self.heap_end {
return core::ptr::null_mut(); // OOM
}
if self.next.compare_exchange_weak(
current,
new_next,
Ordering::SeqCst,
Ordering::Relaxed
).is_ok() {
return aligned as *mut u8;
}
}
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
// Bump 分配器不支持单独释放
}
}
fn align_up(addr: usize, align: usize) -> usize {
(addr + align - 1) & !(align - 1)
}
// 注册全局分配器
#[global_allocator]
static ALLOCATOR: BumpAllocator = BumpAllocator::new();
// 分配错误处理
#[alloc_error_handler]
fn alloc_error(_layout: Layout) -> ! {
loop {}
}
使用现有分配器
// 使用 linked_list_allocator crate
#![no_std]
#![feature(alloc_error_handler)]
extern crate alloc;
use linked_list_allocator::LockedHeap;
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
// 在某处初始化
pub fn init_heap(heap_start: usize, heap_size: usize) {
unsafe {
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
}
}
#[alloc_error_handler]
fn alloc_error_handler(layout: core::alloc::Layout) -> ! {
panic!("allocation error: {:?}", layout)
}
// 现在可以使用 Vec, Box 等
use alloc::vec::Vec;
pub fn test_alloc() -> Vec<i32> {
let mut v = Vec::new();
v.push(1);
v.push(2);
v
}
Panic 处理
基本 panic handler
#![no_std]
use core::panic::PanicInfo;
// 最简单的实现:无限循环
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
// 带调试信息
#[panic_handler]
fn panic_with_info(info: &PanicInfo) -> ! {
// 获取 panic 位置
if let Some(location) = info.location() {
let _file = location.file();
let _line = location.line();
let _column = location.column();
}
// 获取 panic 消息
if let Some(message) = info.message() {
// 使用 message
let _ = message;
}
loop {}
}
输出 panic 信息
#![no_std]
use core::panic::PanicInfo;
use core::fmt::Write;
// 假设我们有一个 UART 输出
struct UartWriter;
impl Write for UartWriter {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for byte in s.bytes() {
unsafe {
// 写入 UART 寄存器(示例地址)
core::ptr::write_volatile(0x1000_0000 as *mut u8, byte);
}
}
Ok(())
}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut writer = UartWriter;
let _ = writeln!(writer, "\n!!! PANIC !!!");
if let Some(location) = info.location() {
let _ = writeln!(
writer,
"Location: {}:{}:{}",
location.file(),
location.line(),
location.column()
);
}
if let Some(message) = info.message() {
let _ = writeln!(writer, "Message: {}", message);
}
// 停止执行
loop {
core::hint::spin_loop();
}
}
panic = "abort" 配置
# Cargo.toml
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
// 使用 abort 时,可以更简单
#![no_std]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
// abort 模式下不会进行栈展开
loop {}
}
嵌入式开发
项目结构
embedded_project/
├── Cargo.toml
├── .cargo/
│ └── config.toml
├── memory.x # 链接脚本
├── build.rs
└── src/
├── main.rs
└── lib.rs
Cortex-M 示例
# Cargo.toml
[package]
name = "embedded_app"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"
# 具体芯片 HAL(以 STM32F4 为例)
# stm32f4xx-hal = { version = "0.17", features = ["stm32f411"] }
[profile.release]
opt-level = "z"
lto = true
debug = true # 保留调试符号
# .cargo/config.toml
[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F411CEUx"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv7em-none-eabihf"
// memory.x
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 128K
}
// src/main.rs
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
#[entry]
fn main() -> ! {
// 获取外设
let peripherals = cortex_m::Peripherals::take().unwrap();
// 主循环
loop {
// 延时
cortex_m::asm::delay(1_000_000);
// 做些事情
}
}
RISC-V 示例
# Cargo.toml
[package]
name = "riscv_app"
version = "0.1.0"
edition = "2021"
[dependencies]
riscv = "0.10"
riscv-rt = "0.11"
panic-halt = "0.2"
# .cargo/config.toml
[target.riscv32imac-unknown-none-elf]
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv32imac-unknown-none-elf"
// src/main.rs
#![no_std]
#![no_main]
use riscv_rt::entry;
use panic_halt as _;
#[entry]
fn main() -> ! {
loop {
riscv::asm::wfi(); // 等待中断
}
}
GPIO 操作示例(embedded-hal)
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
// 使用 embedded-hal traits
use embedded_hal::digital::v2::{OutputPin, InputPin};
// 假设使用 STM32F4 HAL
// use stm32f4xx_hal::{pac, prelude::*, gpio};
#[entry]
fn main() -> ! {
// 伪代码示例
// let dp = pac::Peripherals::take().unwrap();
// let gpioa = dp.GPIOA.split();
// let mut led = gpioa.pa5.into_push_pull_output();
// let button = gpioa.pa0.into_pull_up_input();
loop {
// if button.is_low().unwrap() {
// led.set_high().unwrap();
// } else {
// led.set_low().unwrap();
// }
}
}
中断处理
#![no_std]
#![no_main]
use cortex_m::peripheral::NVIC;
use cortex_m_rt::{entry, exception};
use panic_halt as _;
// 中断处理
#[exception]
fn SysTick() {
static mut COUNT: u32 = 0;
*COUNT += 1;
// 处理定时器中断
}
// 硬件故障处理
#[exception]
fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
// 记录错误信息
let _ = ef;
loop {}
}
// 自定义中断(设备特定)
// #[interrupt]
// fn EXTI0() {
// // 处理外部中断
// }
#[entry]
fn main() -> ! {
let mut peripherals = cortex_m::Peripherals::take().unwrap();
// 配置 SysTick
let sysclk = 8_000_000; // 8 MHz
peripherals.SYST.set_reload(sysclk / 1000 - 1); // 1ms
peripherals.SYST.clear_current();
peripherals.SYST.enable_counter();
peripherals.SYST.enable_interrupt();
loop {
cortex_m::asm::wfi();
}
}
裸机/OS 开发
基本裸机程序(x86_64)
# Cargo.toml
[package]
name = "bare_metal"
version = "0.1.0"
edition = "2021"
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
// x86_64-bare_metal.json(自定义目标)
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
// src/main.rs
#![no_std]
#![no_main]
#![feature(asm_const)]
use core::panic::PanicInfo;
// VGA 文本模式缓冲区
const VGA_BUFFER: *mut u8 = 0xb8000 as *mut u8;
#[no_mangle]
pub extern "C" fn _start() -> ! {
// 清屏
for i in 0..(80 * 25 * 2) {
unsafe {
*VGA_BUFFER.add(i) = 0;
}
}
// 打印 "Hello"
let hello = b"Hello, Bare Metal!";
for (i, &byte) in hello.iter().enumerate() {
unsafe {
*VGA_BUFFER.add(i * 2) = byte;
*VGA_BUFFER.add(i * 2 + 1) = 0x0f; // 白色前景
}
}
loop {
unsafe {
core::arch::asm!("hlt");
}
}
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
VGA 文本缓冲区抽象
#![no_std]
use core::fmt::{self, Write};
use core::ptr;
const VGA_WIDTH: usize = 80;
const VGA_HEIGHT: usize = 25;
const VGA_BUFFER_ADDR: usize = 0xb8000;
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
#[repr(transparent)]
#[derive(Clone, Copy)]
struct ColorCode(u8);
impl ColorCode {
const fn new(foreground: Color, background: Color) -> Self {
ColorCode((background as u8) << 4 | (foreground as u8))
}
}
#[repr(C)]
#[derive(Clone, Copy)]
struct ScreenChar {
ascii: u8,
color: ColorCode,
}
pub struct Writer {
col: usize,
row: usize,
color: ColorCode,
buffer: &'static mut [[ScreenChar; VGA_WIDTH]; VGA_HEIGHT],
}
impl Writer {
pub fn new() -> Self {
Writer {
col: 0,
row: 0,
color: ColorCode::new(Color::White, Color::Black),
buffer: unsafe { &mut *(VGA_BUFFER_ADDR as *mut _) },
}
}
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => self.new_line(),
byte => {
if self.col >= VGA_WIDTH {
self.new_line();
}
self.buffer[self.row][self.col] = ScreenChar {
ascii: byte,
color: self.color,
};
self.col += 1;
}
}
}
fn new_line(&mut self) {
if self.row < VGA_HEIGHT - 1 {
self.row += 1;
} else {
// 滚动
for row in 1..VGA_HEIGHT {
for col in 0..VGA_WIDTH {
self.buffer[row - 1][col] = self.buffer[row][col];
}
}
// 清空最后一行
for col in 0..VGA_WIDTH {
self.buffer[VGA_HEIGHT - 1][col] = ScreenChar {
ascii: b' ',
color: self.color,
};
}
}
self.col = 0;
}
pub fn clear(&mut self) {
for row in 0..VGA_HEIGHT {
for col in 0..VGA_WIDTH {
self.buffer[row][col] = ScreenChar {
ascii: b' ',
color: self.color,
};
}
}
self.col = 0;
self.row = 0;
}
}
impl Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
match byte {
0x20..=0x7e | b'\n' => self.write_byte(byte),
_ => self.write_byte(0xfe), // ■
}
}
Ok(())
}
}
// 全局写入器
use spin::Mutex;
lazy_static::lazy_static! {
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer::new());
}
// 打印宏
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {
$crate::vga::_print(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
WRITER.lock().write_fmt(args).unwrap();
}
简单内存管理
#![no_std]
use core::alloc::{GlobalAlloc, Layout};
use core::ptr::NonNull;
// 简单的固定块分配器
pub struct FixedBlockAllocator {
// 不同大小的块
blocks_16: FreeList<16>,
blocks_64: FreeList<64>,
blocks_256: FreeList<256>,
blocks_1024: FreeList<1024>,
}
struct FreeList<const SIZE: usize> {
head: Option<NonNull<FreeNode>>,
area_start: usize,
area_end: usize,
}
struct FreeNode {
next: Option<NonNull<FreeNode>>,
}
impl<const SIZE: usize> FreeList<SIZE> {
pub const fn new() -> Self {
FreeList {
head: None,
area_start: 0,
area_end: 0,
}
}
pub unsafe fn init(&mut self, start: usize, size: usize) {
self.area_start = start;
self.area_end = start + size;
// 初始化空闲链表
let block_count = size / SIZE;
for i in 0..block_count {
let block_addr = start + i * SIZE;
self.push(block_addr as *mut FreeNode);
}
}
unsafe fn push(&mut self, node: *mut FreeNode) {
(*node).next = self.head;
self.head = Some(NonNull::new_unchecked(node));
}
fn pop(&mut self) -> Option<*mut u8> {
self.head.map(|node| {
unsafe {
self.head = node.as_ref().next;
node.as_ptr() as *mut u8
}
})
}
}
impl FixedBlockAllocator {
pub const fn new() -> Self {
FixedBlockAllocator {
blocks_16: FreeList::new(),
blocks_64: FreeList::new(),
blocks_256: FreeList::new(),
blocks_1024: FreeList::new(),
}
}
fn find_size_class(size: usize) -> Option<usize> {
match size {
0..=16 => Some(16),
17..=64 => Some(64),
65..=256 => Some(256),
257..=1024 => Some(1024),
_ => None,
}
}
}
unsafe impl GlobalAlloc for FixedBlockAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size().max(layout.align());
// 选择合适大小的块
// 实际实现需要更复杂的逻辑
core::ptr::null_mut()
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// 返回块到对应的空闲链表
}
}
常用 no_std Crate
核心工具
[dependencies]
embassy-executor = { version = "0.5", features = ["arch-cortex-m", "executor-thread"] }
embassy-stm32 = { version = "0.1", features = ["stm32f411ce", "time-driver-any"] }
embassy-time = "0.3"
embassy-sync = "0.6"
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }
# 数据结构
heapless = "0.8" # 固定容量集合
arrayvec = { version = "0.7", default-features = false }
# 同步原语
spin = "0.9" # 自旋锁
lock_api = "0.4"
# 字符串处理
ufmt = "0.2" # 轻量级格式化
# 位操作
bitflags = "2"
bitfield = "0.14"
# 序列化
serde = { version = "1", default-features = false }
postcard = "1" # no_std 友好的序列化
# 哈希
hashbrown = { version = "0.14", default-features = false }
# 错误处理
thiserror-no-std = "2"
# 日志
log = "0.4"
defmt = "0.3" # 嵌入式日志框架
heapless 示例
#![no_std]
use heapless::{Vec, String, FnvIndexMap};
// 固定容量 Vec
fn use_heapless_vec() {
let mut v: Vec<i32, 16> = Vec::new(); // 最多 16 个元素
v.push(1).unwrap();
v.push(2).unwrap();
for item in &v {
// 使用 item
}
}
// 固定容量 String
fn use_heapless_string() {
let mut s: String<64> = String::new();
// 使用 write! 宏
use core::fmt::Write;
write!(s, "Hello, {}!", "world").unwrap();
}
// 固定容量 HashMap
fn use_heapless_map() {
let mut map: FnvIndexMap<&str, i32, 16> = FnvIndexMap::new();
map.insert("one", 1).unwrap();
map.insert("two", 2).unwrap();
if let Some(val) = map.get("one") {
// 使用 val
}
}
// 队列
use heapless::spsc::Queue;
static mut QUEUE: Queue<u32, 8> = Queue::new();
fn producer() {
unsafe {
let mut producer = QUEUE.split().0;
producer.enqueue(42).unwrap();
}
}
fn consumer() {
unsafe {
let mut consumer = QUEUE.split().1;
if let Some(val) = consumer.dequeue() {
// 处理 val
}
}
}
embedded-hal 生态
#![no_std]
use embedded_hal::{
digital::{InputPin, OutputPin, StatefulOutputPin},
delay::DelayNs,
spi::SpiDevice,
i2c::I2c,
};
// 泛型驱动(可在任何平台使用)
pub struct SensorDriver<SPI> {
spi: SPI,
}
impl<SPI, E> SensorDriver<SPI>
where
SPI: SpiDevice<Error = E>,
{
pub fn new(spi: SPI) -> Self {
SensorDriver { spi }
}
pub fn read_value(&mut self) -> Result<u16, E> {
let mut buf = [0u8; 2];
self.spi.transfer_in_place(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}
}
// LED 驱动
pub struct Led<PIN> {
pin: PIN,
}
impl<PIN: OutputPin> Led<PIN> {
pub fn new(pin: PIN) -> Self {
Led { pin }
}
pub fn on(&mut self) -> Result<(), PIN::Error> {
self.pin.set_high()
}
pub fn off(&mut self) -> Result<(), PIN::Error> {
self.pin.set_low()
}
}
impl<PIN: StatefulOutputPin> Led<PIN> {
pub fn toggle(&mut self) -> Result<(), PIN::Error> {
self.pin.toggle()
}
}
// 延时闪烁
pub fn blink<PIN, DELAY>(led: &mut Led<PIN>, delay: &mut DELAY, times: u32)
where
PIN: OutputPin,
DELAY: DelayNs,
{
for _ in 0..times {
led.on().ok();
delay.delay_ms(500);
led.off().ok();
delay.delay_ms(500);
}
}
defmt 日志
[dependencies]
defmt = "0.3"
defmt-rtt = "0.4" # RTT 传输
panic-probe = { version = "0.3", features = ["print-defmt"] }
#![no_std]
#![no_main]
use defmt::{info, warn, error, debug, trace};
use defmt_rtt as _;
use panic_probe as _;
#[cortex_m_rt::entry]
fn main() -> ! {
info!("Application started");
let value = 42;
debug!("Value: {}", value);
// 结构化日志
#[derive(defmt::Format)]
struct SensorReading {
temperature: i16,
humidity: u8,
}
let reading = SensorReading {
temperature: 25,
humidity: 60,
};
info!("Sensor reading: {:?}", reading);
loop {
trace!("Loop iteration");
cortex_m::asm::wfi();
}
}
测试 no_std 代码
主机测试
// src/lib.rs
#![cfg_attr(not(test), no_std)]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
}
QEMU 测试
# Cargo.toml
[package]
name = "no_std_tests"
version = "0.1.0"
edition = "2021"
[[test]]
name = "should_panic"
harness = false
[[test]]
name = "integration"
harness = false
[dependencies]
# ...
[dev-dependencies]
x86_64 = "0.14"
uart_16550 = "0.2"
// tests/should_panic.rs
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(test_runner)]
#![reexport_test_harness_main = "test_main"]
use core::panic::PanicInfo;
#[no_mangle]
pub extern "C" fn _start() -> ! {
test_main();
loop {}
}
pub fn test_runner(tests: &[&dyn Fn()]) {
for test in tests {
test();
// 如果没有 panic,测试失败
exit_qemu(QemuExitCode::Failed);
}
exit_qemu(QemuExitCode::Success);
}
fn exit_qemu(exit_code: QemuExitCode) {
use x86_64::instructions::port::Port;
unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
}
#[repr(u32)]
enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
exit_qemu(QemuExitCode::Success);
loop {}
}
#[test_case]
fn should_panic() {
panic!("This should panic");
}
使用 probe-rs 测试
# 安装
cargo install probe-rs --features cli
# 运行测试
cargo test --target thumbv7em-none-eabihf
优化技巧
减小二进制大小
# Cargo.toml
[profile.release]
opt-level = "z" # 优化大小
lto = true # 链接时优化
codegen-units = 1 # 单代码生成单元
panic = "abort" # 不需要展开
strip = true # 去除符号
[profile.release.build-override]
opt-level = "z"
// 避免泛型膨胀
fn process<T: AsRef<[u8]>>(data: T) {
process_inner(data.as_ref())
}
// 非泛型内部实现
fn process_inner(data: &[u8]) {
// 实际逻辑
}
// 使用 #[inline(never)] 防止内联导致的膨胀
#[inline(never)]
fn large_function() {
// ...
}
零成本抽象
#![no_std]
// 编译时常量
const BUFFER_SIZE: usize = 256;
// 零开销类型状态
pub struct Uart<STATE> {
_state: core::marker::PhantomData<STATE>,
}
pub struct Disabled;
pub struct Enabled;
impl Uart<Disabled> {
pub fn enable(self) -> Uart<Enabled> {
// 配置硬件
Uart { _state: core::marker::PhantomData }
}
}
impl Uart<Enabled> {
pub fn send(&mut self, byte: u8) {
// 发送数据
}
pub fn disable(self) -> Uart<Disabled> {
Uart { _state: core::marker::PhantomData }
}
}
// 编译时保证:只有 Enabled 状态可以 send
内存对齐优化
#![no_std]
// 手动控制对齐
#[repr(align(4))]
struct Aligned4 {
data: [u8; 3],
}
// 紧凑结构体
#[repr(packed)]
struct Packed {
a: u8,
b: u32,
}
// C 兼容布局
#[repr(C)]
struct CCompatible {
field1: u32,
field2: u16,
field3: u8,
}
好了,你现在已经了解 Rust 的基本使用了,请用它来写一个操作系统吧!