Rust语言编程

韩乔落

前言

本文面向有编程经验的Rust学习者,使用 Cargo 作为包管理器。

参考链接

[0]Rust Course

[1]rfcs

[2]Rust权威指南(第二版)

[3]Rust算法教程

[4]rust-lang-learn

[5]rust-lang-blog

Rust中的变量

Hello world!

1
2
3
fn main() {
println!("Hello, world!");
}

变量命名

在命名方面,和其它语言没有区别,不过当给变量命名时,需要遵循 Rust 命名规范,也不能使用 Rust关键字

1
let a = "hello world"

变量绑定

上面这条语句将 "hello world" 字符串绑定给了变量a"hello world" 的所有权也就属于a。在Rust中任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人。

1
let a = "hello world"

所有权转移后,该对象之前的主人就会丧失对该对象的所有权。

1
2
3
4
5
6
7
8
9
10
11
12
13
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 的变量在默认情况下是不可变的

1
2
3
4
5
6
fn main() {
let a = 10;
println!("a is a number: {}", a);
// a = 20; // ❌ 编译错误!无法修改不可变变量
println!("a is a number: {}", a);
}

我们可以通过 mut 关键字让变量变为可变的

1
2
3
4
5
6
fn main() {
let mut a = 10; // 可变变量
println!("a is a number: {}", a);
a = 20;
println!("a is a number: {}", a);
}

忽略未使用的变量

如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 BUG。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头

1
2
3
4
fn main() {
let _x = 5;
let y = 10; // #[warn(unused_variables)]
}

变量解构

let 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:

1
2
3
4
5
6
7
8
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 版本后,我们可以在赋值语句的左式中使用元组、切片和结构体模式了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性):

1
const MAX_POINTS: u32 = 100_000;

常量可以在任意作用域内声明,包括全局作用域,在声明的作用域内,常量在程序运行的整个过程中都有效。对于需要在多处代码共享一个不可变的值时非常有用,例如游戏中允许玩家赚取的最大点数或光速。

变量遮蔽

Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
  • 布尔类型:truefalse
  • 字符类型:表示单个 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 类型,因此你可以首选它,同时该类型也往往是性能最好的。isizeusize 的主要应用场景是用作集合的索引。

整型溢出

假设有一个 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_* 方法,可以限定计算后的结果不超过目标类型的最大值或低于最小值,例如:
1
2
assert_eq!(100u8.saturating_add(1), 101);
assert_eq!(u8::MAX.saturating_add(127), u8::MAX);

下面是一个演示wrapping_*方法的示例:

1
2
3
4
5
fn main() {
let a : u8 = 255;
let b = a.wrapping_add(20);
println!("{}", b); // 19
}

浮点类型

浮点类型数字 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: f32f64,分别为 32 位和 64 位大小。默认浮点类型是 f64,在现代的 CPU 中它的速度与 f32 几乎相同,但精度更高。

下面是一个演示浮点数的示例:

1
2
3
4
5
fn main() {
let x = 2.0; // f64

let y: f32 = 3.0; // f32
}

浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。

浮点陷阱

浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:

  1. 浮点数往往是你想要数字的近似表达 浮点数类型是基于二进制实现的,但是我们想要计算的数字往往是基于十进制,例如 0.1 在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果你想要表达完全精准的真实数字,只有使用无限精度的浮点数才行
  2. 浮点数在某些特性上是反直觉的 例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用 >>= 等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为 f32f64 上的比较运算实现的是 std::cmp::PartialEq 特征(类似其他语言的接口),但是并没有实现 std::cmp::Eq 特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:

Rust 的 HashMap 数据结构,是一个 KV 类型的 Hash Map 实现,它对于 K 没有特定类型的限制,但是要求能用作 K 的类型必须实现了 std::cmp::Eq 特征,因此这意味着你无法使用浮点数作为 HashMapKey,来存储键值对,但是作为对比,Rust 的整数类型、字符串类型、布尔类型都实现了该特征,因此可以作为 HashMapKey

为了避免上面说的两个陷阱,你需要遵守以下准则:

  • 避免在浮点数上测试相等性
  • 当结果在数学上可能存在未定义时,需要格外的小心。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 的结果是 3e99999a0.3 也是 3e99999a,因此 f32 下的 0.1 + 0.2 == 0.3 通过测试,但是到了 f64 类型时,结果就不一样了,因为 f64 二进制精度高很多,导致了 0.1 + 0.2 并不严格等于 0.3,它们可能在小数点 N 位后存在误差。0.1 + 0.24 结尾,但是 0.33结尾,这个区别导致 f64 下的测试失败了,并且抛出了异常。

NaN

对于数学上未定义的结果,例如对负数取平方根 -42.1.sqrt() ,会产生一个特殊的结果:Rust 的浮点数类型使用 NaN (not a number) 来处理这些情况。

**所有跟 NaN 交互的操作,都会返回一个 NaN**,而且 NaN 不能用来比较,下面的代码会崩溃:

1
2
3
4
fn main() {
let x = (-42.0_f32).sqrt();
assert_eq!(x, x);
}

出于防御性编程的考虑,可以使用 is_nan() 等方法,可以用来判断一个数值是否是 NaN

1
2
3
4
5
6
fn main() {
let x = (-42.0_f32).sqrt();
if x.is_nan() {
println!("未定义的数学行为")
}
}

数字运算

Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 的 >>>

  • 右移 >> 的行为完全由操作数的类型决定

  • 如需对有符号数进行逻辑右移,可转换为无符号类型后再右移

  • 复合赋值:支持 &=|=^=<<=>>=

  • 左移溢出:超出类型范围的位会被丢弃

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
}

关于移位运算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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,它的用途很简单,常常用于循环中:

1
2
3
for i in 1..=5 {
println!("{}",i);
}

序列只允许用于数字或字符类型,原因是它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。如下是一个使用字符类型序列的例子:

1
2
3
for i in 'a'..='z' {
println!("{}",i);
}

字符类型

Rust 的字符不仅仅是 ASCII,所有的 Unicode 值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode 值的范围从 U+0000 ~ U+D7FFU+E000 ~ U+10FFFF。由于 Unicode 都是 4 个字节编码,因此Rust的字符类型也是占用 4 个字节。

1
2
3
4
5
6
fn main() {
let c = 'z';
let z = 'ℤ';
let g = '国';
let heart_eyed_cat = '😻';
}

布尔类型

Rust 中的布尔类型有两个可能的值:truefalse,布尔值占用内存的大小为 1 个字节:

1
2
3
4
5
6
7
8
9
fn main() {
let t = true;

let f: bool = false; // 使用类型标注,显式指定f的类型

if f {
println!("这是段毫无意义的代码");
}
}

单元类型

单元类型就是 (),唯一的值也是 ()

1
2
3
4
5
6
7
8
9
10
11
// ✓ 返回单元类型 ()
fn normal() -> () { }
fn main() { }
println!("hello");

// ✓ 发散函数(永不返回)
fn diverge() -> ! {
panic!("crash!");
// 或 loop {}
// 或 std::process::exit(0)
}
特性 说明
定义 () 既是类型也是唯一的值
大小 0 字节,不占内存
main 返回 返回 (),不是”无返回值”
用途 占位符、表示”不关心返回值”
区别 -> () 有返回值,-> ! 才是永不返回(发散函数,即没有返回值)

语句和表达式

Rust 的函数体是由一系列语句组成,最后由一个表达式来返回值,例如:

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

语句会执行一些操作但是不会返回一个值,而表达式会在求值后返回一个值,因此在上述函数体的三行代码中,前两行是语句,最后一行是表达式。

对于 Rust 语言而言,这种基于语句(statement)和表达式(expression)的方式是非常重要的,你需要能明确的区分这两个概念,但是对于很多其它语言而言,这两个往往无需区分。基于表达式是函数式语言的重要特征,表达式总要返回值

语句

1
2
3
let a = 8;
let b: Vec<f64> = Vec::new();
let (a, c) = ("hi", false);

以上都是语句,它们完成了一个具体的操作,但是并没有返回值,因此是语句。

由于 let 是语句,因此不能将 let 语句赋值给其它值,如下形式是错误的:

1
let b = (let a = 8);// variable declaration using `let` is a statement `let`是一条语句

表达式

表达式会进行求值,然后返回一个值。例如 5 + 6,在求值后,返回值 11,因此它就是一条表达式。

表达式可以成为语句的一部分,例如 let y = 6 中,6 就是一个表达式,它在求值后返回一个值 6。调用一个函数是表达式,因为会返回一个值,调用宏也是表达式,用花括号包裹最终返回一个值的语句块也是表达式,总之,能返回值,它就是表达式:

1
2
3
4
5
6
7
8
fn main() {
let y = {
let x = 3;
x + 1
};

println!("The value of y is: {}", y); // 4
}

上面使用一个语句块表达式将值赋给 y 变量,语句块长这样:

1
2
3
4
{
let x = 3;
x + 1
}

该语句块是表达式的原因是:它的最后一行是表达式,返回了 x + 1 的值,注意 x + 1 不能以分号结尾,否则就会从表达式变成语句, 表达式不能包含分号。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也不会返回一个值,请牢记!

最后,表达式如果不返回任何值,会隐式地返回一个 ()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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: i32j: i32,参数类型和返回值类型都是 i32

1
2
3
fn add(i: i32, j: i32) -> i32 {
i + j
}

img

函数要点

  • 函数名和变量名使用蛇形命名法(snake_case),例如 fn add_two() {}
  • 函数的位置可以随便放,Rust 不关心我们在哪里定义了函数,只要有定义即可
  • 每个函数参数都需要标注类型

函数参数

Rust 是静态类型语言,因此需要你为每一个函数参数都标识出它的具体类型,例如:

1
2
3
4
5
6
7
8
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 函数有两个参数,其中 xi32 类型,yf32 类型,然后在该函数内部,打印出这两个值。这里去掉 x 或者 y 的任何一个的类型,都会报错。

函数返回

在 Rust 中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。

函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用 return 提前返回,下面的函数使用最后一条表达式来返回一个值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}

返回单元类型

单元类型 (),是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值:

  • 函数没有返回值,那么返回一个 ()
  • 通过 ; 结尾的语句返回一个 ()
1
2
3
4
5
6
7
fn plus_number(x:i32) {
x + 5;
}
// 或
fn plus_number(x:i32) -> () {
x + 5;
}

永不返回的发散函数 !

当用 ! 作函数返回类型的时候,表示该函数永不返回( diverging functions ),特别的,这种语法往往用做会导致程序崩溃的函数:

1
2
3
fn dead_end() -> ! {
panic!("dead_end");
}

下面的函数创建了一个无限循环,该循环永不跳出,因此函数也永不返回:

1
2
3
4
5
fn forever() -> ! {
loop {
//...
};
}

所有权和借用

所有权

Rust 通过所有权来管理内存,这种检查只发生在编译期,因此对于程序运行期,不会有性能上的损失。

  1. Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
  2. 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
  3. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)

变量作用域

作用域是一个变量在程序中有效的范围,假如有这样一个变量:

1
let s = "hello";

变量 s 绑定到了一个字符串字面值,该字符串字面值是硬编码到程序代码中的。s 变量从声明的点开始直到当前作用域的结束都是有效的:

1
2
3
4
5
{                      // s 在这里无效,它尚未声明
let s = "hello"; // 从此处起,s 是有效的

// 使用 s
} // 此作用域已结束,s不再有效

简而言之,s 从创建开始就有效,然后有效期持续到它离开作用域为止,可以看出,就作用域来说,Rust 语言跟其他编程语言没有区别。

所有权转移

先来看一段代码:

1
2
let x = 5;
let y = x;

这段代码并没有发生所有权的转移,原因很简单: 代码首先将 5 绑定到变量 x,接着拷贝 x 的值赋给 y,最终 xy 都等于 5,因为整数是 Rust 基本数据类型,是固定大小的简单值,因此这两个值都是通过自动拷贝的方式来赋值的,都被存在栈中,完全无需在堆上分配内存。整个过程中的赋值都是通过值拷贝的方式完成(发生在栈中),因此并不需要所有权转移。

移动(move)

然后再来看一段代码:

1
2
3
4
5
6
fn main() {
let s1 = String::from("hello");
let s2 = s1;

println!("{}, world!", s1);
}

String 类型是一个复杂类型,由存储在栈中的堆指针字符串长度字符串容量共同组成。String 类型指向了一个堆上的空间,这里存储着它的真实数据。s1 被赋予 s2 后,Rust 认为 s1 不再有效,因此也无需在 s1 离开作用域后 drop 任何东西,这就是把所有权从 s1 转移给了 s2s1 在被赋予 s2 后就马上失效了

1
2
3
4
5
6
7
8
9
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 的方法。

1
2
3
4
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,它的值是 truefalse
  • 所有浮点数类型,比如 f64
  • 字符类型,char
  • 元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32)Copy 的,但 (i32, String) 就不是
  • 不可变引用 &T ,但是注意:可变引用 &mut T 是不可以 Copy的。

一个类型可以是 Copy 的,当且仅当

  1. ✅ 所有字段都实现了 Copy
  2. ✅ 不拥有任何堆上的资源
  3. ✅ 没有实现 Drop trait
  4. ✅ 是固定大小的(Sized

函数传值和返回

将值传递给函数,一样会发生 移动 或者 复制,就跟 let 语句一样,下面的代码展示了所有权、作用域的规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 移出作用域。不会有特殊操作

同样的,函数返回值也有所有权,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 所使用的值:

1
2
3
4
5
6
7
fn main() {
let x = 5;
let y = &x;

assert_eq!(5, x);
assert_eq!(5, *y);
}

变量 x 存放了一个 i325yx 的一个引用。可以断言 x 等于 5。然而,如果希望对 y 的值做出断言,必须使用 *y 来解出引用所指向的值(也就是解引用)。一旦解引用了 y,就可以访问 y 所指向的整型值并可以与 5 做比较。

不可变引用

下面的代码,我们用 s1 的引用作为参数传递给 calculate_length 函数,而不是把 s1 的所有权转移给该函数:

1
2
3
4
5
6
7
8
9
10
11
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 的引用,但是并不拥有它。因为并不拥有这个值,当引用离开作用域后,其指向的值也不会被丢弃。

可变引用

1
2
3
4
5
6
7
8
9
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 的函数。可变引用同时只能存在一个,可变引用并不是随心所欲、想用就用的,它有一个很大的限制: 同一作用域,特定数据只能有一个可变引用

1
2
3
4
5
6
let mut s = String::from("hello");

let r1 = &mut s;
let r2 = &mut s;

println!("{}, {}", r1, r2);
1
2
3
4
5
6
7
8
9
10
11
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 第一个借用在这里使用

在数据被可变借用期间,原所有者完全失去对数据的访问权,既不能读也不能写。

1
2
3
4
5
6
let mut s = String::from("hello");

let r1 = &mut s;
s.push_str(", world!");

println!("{}", r1);
1
2
3
4
5
6
7
8
9
10
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

复合类型

字符串

Rust 中的字符串主要有两种核心类型:String&str

两种核心字符串类型

&str(字符串切片)

1
2
3
4
5
6
// 字符串字面量,存储在程序的只读内存区
let s1: &str = "Hello, Rust!";

// 也可以是 String 的切片引用
let string = String::from("Hello");
let s2: &str = &string[0..3]; // "Hel"

特点:

  • 不可变引用(borrowed)
  • 固定大小的视图
  • 通常作为函数参数使用

String(可增长字符串)

1
2
3
4
5
6
7
8
9
// 创建空字符串
let mut s = String::new();

// 从字面量创建
let s = String::from("Hello");
let s = "Hello".to_string();

// 带容量预分配
let s = String::with_capacity(100);

特点:

  • 堆上分配,可增长
  • 拥有所有权(owned)
  • 可修改

内存布局对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─────────────────────────────────────────────────────────────────────┐
│ 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 常用操作

创建与修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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(); // 清空
}

字符串连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 编码,这带来一些特殊行为:

字符与字节的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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) { ... }
}

索引限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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('你')
}

类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 强制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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,更通用
}

性能与实践

容量预分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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());
}

避免不必要的克隆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ❌ 不必要的克隆
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)
}
}

函数参数建议

1
2
3
4
5
6
7
8
9
10
11
12
13
// ✅ 推荐:接受 &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 中非常核心的概念,它是对连续内存序列的一个引用视图,不拥有数据所有权。

切片基本概念

什么是切片

1
2
3
4
5
6
7
8
fn main() {
let arr = [1, 2, 3, 4, 5];

// 切片:对数组部分或全部元素的引用
let slice: &[i32] = &arr[1..4]; // [2, 3, 4]

println!("{:?}", slice);
}

切片的本质

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────────────────────────────────────┐
│ 切片是"胖指针" │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 普通指针 (8 bytes) 切片/胖指针 (16 bytes) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ ptr │ │ ptr │ ──► 指向数据起始位置 │
│ └──────────────┘ ├──────────────┤ │
│ │ len │ ──► 元素数量 │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

切片的内存布局

1
2
3
4
5
6
7
8
9
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));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
内存示意图:

arr (栈上连续内存):
┌──────┬──────┬──────┬──────┬──────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└──────┴──────┴──────┴──────┴──────┘
[0] [1] [2] [3] [4]
▲ │
│ │
└─────────────┘
slice 覆盖范围 [1..4]

slice (胖指针):
┌─────────────────┐
│ ptr ──────────────► 指向 arr[1] 的地址
├─────────────────┤
│ len = 3 │
└─────────────────┘

切片语法详解

Range 语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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]

1
2
3
4
5
6
7
8
9
10
11
12
13
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]
}

切片与所有权类型的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────────────┐
│ 所有权类型 vs 借用类型 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Owned │ │ Borrowed │ │
│ │ (拥有数据) │ ──Deref──► │ (借用视图) │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ String │ ──────────────► │ &str │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Vec<T> │ ──────────────► │ &[T] │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ [T; N] │ ──────────────► │ &[T] │ │
│ │ (数组) │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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(); // 显式转换
}

切片常用方法

基础方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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!
}

迭代与遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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]
}

查找与搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)
}

分割与连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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]
// ...
}

排序与反转(需要可变切片)

1
2
3
4
5
6
7
8
9
10
11
12
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]
}

复制与填充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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]
}

函数参数中的切片

切片作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ✅ 推荐:接受切片,更通用
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])); // ✅
}

可变切片参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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]
}

返回切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 返回切片必须关联生命周期
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 中一种重要的复合类型,可以将多个不同类型的值组合成一个单一的复合值。

元组基本概念

什么是元组

1
2
3
4
5
6
fn main() {
// 元组:固定长度、可包含不同类型的值的集合
let tuple: (i32, f64, char, &str) = (42, 3.14, 'R', "Rust");

println!("{:?}", tuple); // (42, 3.14, 'R', "Rust")
}

元组的特点

1
2
3
4
5
6
7
8
9
10
11
┌─────────────────────────────────────────────────────────────────────┐
│ 元组特性 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ✓ 固定长度 - 一旦声明,长度不可改变 │
│ ✓ 异构类型 - 每个元素可以是不同类型 │
│ ✓ 有序集合 - 元素按位置索引访问 │
│ ✓ 值类型 - 存储在栈上(如果所有元素都是栈类型) │
│ ✓ 可解构 - 支持模式匹配解构 │
│ │
└─────────────────────────────────────────────────────────────────────┘

元组的创建与类型

基本创建方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
}

元组类型签名

1
2
3
4
5
6
7
8
9
10
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; // 元素类型不同
}

内存布局

1
2
3
4
5
6
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));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────────┐
│ (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 的对齐) │
│ │
└─────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 使用 #[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
}

访问元组元素

索引访问(点号语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
}

可变元组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)
}

单元类型 ()

我们之间讲到的单元类型也是一种特殊的元组。

单元类型的概念

1
2
3
4
5
6
7
8
9
10
fn main() {
// () 是一种特殊的元组,称为单元类型(unit type)
let unit: () = ();

// 单元类型的大小为 0
println!("() 的大小: {} bytes", std::mem::size_of::<()>()); // 0

// 单元类型只有一个值,也是 ()
assert_eq!(unit, ());
}

单元类型的用途

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 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(())
}

元组与函数

作为函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 接受元组参数
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);
}

作为返回值(多返回值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 返回多个值
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);
}

交换值的惯用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 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
}
1
2
3
4
5
6
7
8
┌────────────────┬─────────────────┬─────────────────┬──────────────────┐
│ 特性 │ 常规结构体 │ 元组结构体 │ 单元结构体 │
├────────────────┼─────────────────┼─────────────────┼──────────────────┤
│ 字段名称 │ 有 │ 无 │ 无 │
│ 访问方式 │ .name │ .0 .1 .2 │ N/A │
│ 内存大小 │ 字段和+对齐 │ 字段和+对齐 │ 0 │
│ 主要用途 │ 数据建模 │ NewType/简单 │ 标记/trait │
└────────────────┴─────────────────┴─────────────────┴──────────────────┘

创建与初始化

基本创建与字段简写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}

结构体更新语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
}

字段访问与修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 = &rect;
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;
}

所有权与借用

字段的移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}

部分借用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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); // ✅ 借用不同字段,允许
}

内存布局

默认布局

1
2
3
4
5
6
7
8
9
10
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
}
1
2
3
4
5
6
7
Rust 编译器可能重排字段以优化内存:

┌────────────────────────────┬──────┬──────┬──────────────┐
│ b (u32) │ a │ c │ padding │
│ 4 bytes │ 1B │ 1B │ 2 bytes │
└────────────────────────────┴──────┴──────┴──────────────┘
总大小: 8 bytes(优化后)

repr 属性控制布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
}

打印结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
// 派生 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 中定义”多选一”类型的方式,一个值在某一时刻只能是其中一个变体。

基本枚举定义

简单枚举(无关联数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 定义枚举
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!("正在运行中...");
}
}

带关联数据的枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 每个变体可以携带不同类型、不同数量的数据
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),
}
}

三种变体形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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),
}
}
1
2
3
4
5
6
7
┌─────────────────┬────────────────────┬─────────────────────────────┐
│ 变体类型 │ 语法 │ 示例 │
├─────────────────┼────────────────────┼─────────────────────────────┤
│ 单元变体 │ Name │ Quit, None, Empty │
│ 元组变体 │ Name(T1, T2) │ Some(42), Move(10, 20) │
│ 结构体变体 │ Name { f: T } │ Point { x: 1, y: 2 } │
└─────────────────┴────────────────────┴─────────────────────────────┘

枚举的判别值

默认判别值

1
2
3
4
5
6
7
8
9
10
11
12
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
}

自定义判别值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
}

部分指定(自动递增)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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> - 可选值

定义

1
2
3
4
5
// 标准库中的定义
enum Option<T> {
Some(T), // 有值,包含一个 T 类型的值
None, // 无值
}

为什么需要 Option?

1
2
3
4
5
6
7
8
9
10
11
12
// 其他语言中的问题: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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 不能直接使用内部值

1
2
3
4
5
6
7
8
9
10
11
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
}

基本方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
}

常见使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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> - 错误处理

定义

1
2
3
4
5
// 标准库中的定义
enum Result<T, E> {
Ok(T), // 成功,包含成功值 T
Err(E), // 失败,包含错误值 E
}

为什么需要 Result?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 其他语言的问题:异常可能被忽略或遗漏
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
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("失败了")
}

基本方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
}

常见使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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("年龄不合理")
}

相互转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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);
}

内存布局

基本原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
}
1
2
3
4
5
6
7
8
9
10
11
枚举内存布局 = 判别值(tag)+ 数据(payload)

WithData 布局示意:
┌──────────┬─────────────────────────────────────────┐
│ tag │ payload │
│ (8B) │ (最大变体的大小) │
├──────────┼─────────────────────────────────────────┤
│ 0 │ (Empty: 无数据,但空间仍保留) │
│ 1 │ Number: i32 (4B) + padding │
│ 2 │ Text: String (24B) │
└──────────┴─────────────────────────────────────────┘

数组

数组是固定长度、存储在栈上的同类型元素集合,长度是类型的一部分。

基本定义与创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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());
}

长度是类型的一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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); // ❌ 类型不匹配
}
1
2
3
4
5
6
7
8
9
┌─────────────────────────────────────────────────────────┐
│ 数组类型 = 元素类型 + 长度 │
│ │
│ [i32; 3] → 3 个 i32,共 12 bytes │
│ [i32; 5] → 5 个 i32,共 20 bytes │
│ [u8; 100] → 100 个 u8,共 100 bytes │
│ │
│ 这三个是完全不同的类型! │
└─────────────────────────────────────────────────────────┘

元素访问

索引访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
}
}

遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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]
}

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)
}

排序与修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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]
}

数组与切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
}
1
2
3
4
5
6
7
8
9
┌─────────────────────────────────────────────────────────┐
│ 数组 [T; N] vs 切片 &[T] │
├─────────────────────────────────────────────────────────┤
│ 长度编译时固定 长度运行时确定 │
│ 存储在栈上 是引用(胖指针) │
│ 大小 = N * sizeof(T) 大小 = 2 * 指针 │
│ 类型包含长度 类型不包含长度 │
│ [i32; 3] ≠ [i32; 5] &[i32] 通用 │
└─────────────────────────────────────────────────────────┘

多维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
}

内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
}
1
2
3
4
5
6
7
8
9
10
内存布局(栈上连续存储):

arr: [1i32, 2, 3, 4, 5]

地址: 0x1000 0x1004 0x1008 0x100C 0x1010
┌────────┬────────┬────────┬────────┬────────┐
│ 1 │ 2 │ 3 │ 4 │ 5 │
│ (i32) │ (i32) │ (i32) │ (i32) │ (i32) │
└────────┴────────┴────────┴────────┴────────┘
│◄──────────────── 20 bytes ───────────────►│

流程控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────────────────┐
│ 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 类型,不会自动转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let number = 42;

// 基本用法
if number > 0 {
println!("正数");
} else if number < 0 {
println!("负数");
} else {
println!("零");
}

// 作为表达式返回值(各分支类型必须一致)
let description = if number % 2 == 0 { "偶数" } else { "奇数" };

// 在 let 语句中使用
let abs_value = if number >= 0 { number } else { -number };

// 嵌套条件
let category = if number > 100 {
"大"
} else {
if number > 10 { "中" } else { "小" }
};

if let 表达式

当只关心一种匹配情况时,if letmatch 更简洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 引入,用于匹配失败时必须发散(提前退出)的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 必须以 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, Error>>) -> String {
let Some(result) = data else {
return String::new();
};
let Ok(content) = result else {
return String::from("error");
};
content
}

matches! 宏

返回 bool 的模式匹配,适合在条件判断中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let c = 'f';

// 检查是否匹配模式
let is_lowercase = matches!(c, 'a'..='z');
let is_letter = matches!(c, 'a'..='z' | 'A'..='Z');
let is_hex = matches!(c, '0'..='9' | 'a'..='f' | 'A'..='F');

// 带守卫条件
let opt = Some(42);
let is_positive = matches!(opt, Some(x) if x > 0);
let is_large = matches!(opt, Some(x) if x > 100);

// 匹配枚举变体
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);

// 在 filter 中使用
let numbers = vec![Some(1), None, Some(3), None, Some(5)];
let valid: Vec<_> = numbers.iter()
.filter(|x| matches!(x, Some(_)))
.collect();

循环控制

loop 无限循环

loop 创建无限循环,必须通过 break 退出,可以返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 基本无限循环
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 条件循环

条件为真时持续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基本用法
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 遍历 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 的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 范围迭代
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 退出循环

退出当前循环,可携带返回值,可指定标签跳出多层循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 基本 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 跳过迭代

跳过当前迭代的剩余部分,进入下一次迭代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 基本 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 函数返回

从函数中提前返回,可携带返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 提前返回
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()
}

标签块表达式

从任意代码块中返回值,不仅限于循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 基本标签块
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 最强大的控制流结构,必须穷尽所有可能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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 => "两位数",
_ => "更大的数",
};

@ 绑定

在匹配的同时将值绑定到变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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!("无值"),
}

模式语法详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 解构元组
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!("退出"),
}

错误处理

? 运算符

简化错误传播,遇到 ErrNone 时提前返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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 模式下直接中止)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 基本用法
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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 中几乎所有代码块都是表达式,最后一行无分号即为返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 基本块表达式
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,代表调用该方法的实例。

1
2
3
4
5
6
┌─────────────────────────────────────────────────────┐
│ 方法 vs 函数 │
├─────────────────────────────────────────────────────┤
│ 函数 (fn) │ 独立存在,不依附于任何类型 │
│ 方法 (method) │ 定义在 impl 块中,绑定到特定类型 │
└─────────────────────────────────────────────────────┘

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 获取所有权 转移 转换类型或消费实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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*,使方法调用更简洁:

1
2
3
4
5
let rect = Rectangle::new(10, 20);

// 以下两种写法等价:
rect.area();
(&rect).area(); // Rust 自动推导

多个 impl 块

一个类型可以有多个 impl 块(常用于 trait 实现分离):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)
}
}

枚举上的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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),
}
}
}

方法链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

核心概念

泛型是一种参数化类型的机制,让代码可以处理多种数据类型,而无需重复编写。

1
2
3
4
5
6
7
8
┌─────────────────────────────────────────────────────────────┐
│ 泛型的价值 │
├─────────────────────────────────────────────────────────────┤
│ ✗ 没有泛型:fn max_i32(a: i32, b: i32) -> i32 │
│ fn max_f64(a: f64, b: f64) -> f64 重复代码! │
├─────────────────────────────────────────────────────────────┤
│ ✓ 使用泛型:fn max<T>(a: T, b: T) -> T 一份代码! │
└─────────────────────────────────────────────────────────────┘

函数泛型

1
2
3
4
5
6
7
8
9
10
// 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
}

结构体泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 单个类型参数
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>
}

枚举泛型

标准库中最经典的两个泛型枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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());
}

方法泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
2
3
4
5
6
7
8
9
10
11
12
13
// 语法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,
{
// ...
}

常见约束模式

1
2
3
4
5
6
7
8
9
┌──────────────────┬────────────────────────────────────────┐
│ 约束形式 │ 含义 │
├──────────────────┼────────────────────────────────────────┤
│ T: Clone │ T 必须实现 Clone
│ T: Clone + Debug │ T 必须同时实现 CloneDebug
│ T: 'static │ T 不包含非静态引用 │
│ T: ?Sized │ T 可以是动态大小类型 │
│ T: Default │ T 可以有默认值 │
└──────────────────┴────────────────────────────────────────┘

实际示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 语法糖

1
2
3
4
5
6
7
8
9
// 参数位置:接受任何实现了 Display 的类型
fn print(item: impl Display) {
println!("{}", item);
}

// 返回位置:返回某个实现了 Iterator 的类型
fn counter() -> impl Iterator<Item = i32> {
(0..10).filter(|x| x % 2 == 0)
}

const 泛型(常量泛型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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
}

默认类型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
// 标准库 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 {
// ...
}
}

单态化(零成本抽象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─────────────────────────────────────────────────────────────┐
│ 编译时单态化 │
├─────────────────────────────────────────────────────────────┤
│ 源代码: │
│ 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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),但更强大。

1
2
3
4
5
6
7
8
┌─────────────────────────────────────────────────────────────┐
│ Trait 的本质 │
├─────────────────────────────────────────────────────────────┤
│ • 定义共享行为的抽象 │
│ • 实现多态(编译时/运行时) │
│ • 约束泛型参数 │
│ • 扩展现有类型的能力 │
└─────────────────────────────────────────────────────────────┘

基本语法

定义 Trait

1
2
3
4
// 定义 trait
trait Summary {
fn summarize(&self) -> String; // 必须实现的方法
}

实现 Trait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 张三"
}

默认实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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());
}

多重约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 返回实现了 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────┬────────────────────────────────────────────┐
│ 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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 自动实现常见 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

1
2
3
4
5
6
7
8
9
10
11
12
#[derive(
Debug, // 调试输出
Clone, // 克隆
Copy, // 复制(需要 Clone)
PartialEq, // 部分相等
Eq, // 完全相等(需要 PartialEq)
PartialOrd, // 部分排序
Ord, // 完全排序(需要 PartialOrd + Eq)
Hash, // 哈希(需要 Eq)
Default, // 默认值
)]
struct Data { /* ... */ }

关联类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 使用关联类型(每个实现只有一种类型)
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 泛型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 关联类型:一个类型只能实现一次
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ✅ 可以:为自己的类型实现外部 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(特征继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 的方法/函数,解决名称冲突问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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);
}

关联常量

1
2
3
4
5
6
7
8
9
10
11
12
13
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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);
}
}
}

集合类型

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────────────────────────────┐
│ Rust 集合类型 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 序列(Sequences) 映射(Maps) 集合(Sets) │
│ ├── Vec<T> ├── HashMap<K,V> ├── HashSet<T> │
│ ├── VecDeque<T> └── BTreeMap<K,V> └── BTreeSet<T> │
│ └── LinkedList<T> │
│ │
│ 其他 │
│ └── BinaryHeap<T> │
│ │
└─────────────────────────────────────────────────────────────────────────┘

序列

vec<T>

VecDeque<T>

LinkedList<T>

映射

HashMap<K,V>

BTreeMap<K,V>

集合

HashSet<T>

BTreeSet<T>

其他

BinaryHeap<T>

生命周期

包和模块

格式化输出

注释与文档

函数式编程

闭包

迭代器

深入类型

智能指针

循环引用

自引用

多线程

多进程

Unsafe 编程

宏编程

异步编程

no_std 编程

自动化测试

Cargo 使用


好了,你现在已经了解 Rust 的基本使用了,请用它来写一个操作系统吧!

版本更新变化

  • Title: Rust语言编程
  • Author: 韩乔落
  • Created at : 2025-01-23 14:48:23
  • Updated at : 2025-12-24 15:42:51
  • Link: https://jelasin.github.io/2025/01/23/Rust语言编程/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Rust语言编程