变量和可变性

Rust 中变量默认是不可变的(immutable),使用 mut 关键字可声明可变变量。

在 Rust 中,变量使用 let 关键字声明。默认情况下,变量一旦赋值就不能再修改,这有助于编写安全且易于推理的代码。

1. 不可变变量

1
2
3
4
5
6
fn main() {
// 使用 let 声明变量,默认不可变
let x = 5;
println!("x 的值是: {}", x);
// x = 6; // ❌ 编译错误!不可变变量不能重新赋值
}

2. 可变变量

1
2
3
4
5
6
7
fn main() {
// 使用 mut 关键字声明可变变量
let mut y = 10;
println!("初始值: {}", y); // 输出 初始值: 10
y = 20; // ✅ 可以重新赋值
println!("修改后: {}", y); // 输出 修改后: 20
}

3. 常量

1
2
3
4
5
6
7
// 常量使用 const 声明,必须标注类型,命名通常全大写
const MAX_POINTS: u32 = 100_000;

fn main() {
println!("最大点数: {}", MAX_POINTS); // 输出 最大点数: 100000
// 常量在整个程序运行期间一直有效,且不能被 mut 修饰
}

4. 变量遮蔽(Shadowing)

1
2
3
4
5
6
7
8
9
fn main() {
let x = 5;
let x = x + 1; // 用 let 重新声明同名变量,遮蔽旧值
println!("x = {}", x); // 输出 x = 6

let spaces = " "; // 字符串类型
let spaces = spaces.len(); // 遮蔽后可以改变类型(而 mut 不能改类型)
println!("空格数: {}", spaces); // 输出 空格数: 3
}

数据类型(标量与复合)

Rust 是静态类型语言,编译时必须知道所有变量的类型。标量类型表示单一值,复合类型表示一组值。

1. 标量类型(Scalar Types)

整数类型

长度 有符号 无符号
8 位 i8 u8
16 位 i16 u16
32 位 i32 u32
64 位 i64 u64
128 位 i128 u128
arch isize usize
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
// 整数类型示例
let a: i32 = -42; // 有符号 32 位整数(默认类型)
let b: u8 = 255; // 无符号 8 位整数(0~255)
let c: usize = 100; // 指针大小的无符号整数

// 字面量可加分隔符提高可读性
let d = 1_000_000; // 等同于 1000000

// 不同进制表示
let hex = 0xff; // 十六进制 255
let oct = 0o77; // 八进制 63
let bin = 0b1111_0000; // 二进制 240
let byte = b'A'; // 字节字面量 65

println!("a={}, b={}, c={}, d={}", a, b, c, d);
println!("hex={}, oct={}, bin={}, byte={}", hex, oct, bin, byte);
}

浮点数类型

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let x: f32 = 3.14; // 单精度浮点数
let y: f64 = 2.71828; // 双精度浮点数(默认类型)
let z = 1.0; // 默认 f64

// 基本数学运算
println!("加: {}", x + 1.0); // 输出 4.14
println!("减: {}", y - 1.0); // 输出 1.71828
println!("乘: {}", 2.5 * 2.0); // 输出 5
println!("除: {}", 5.0 / 2.0); // 输出 2.5
println!("取余: {}", 7.0 % 3.0); // 输出 1
}

布尔类型

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let t: bool = true;
let f: bool = false; // 显式类型标注
let is_ok = true; // 类型推断

// 布尔值常用于条件判断
if is_ok {
println!("一切正常!");
}
println!("t={}, f={}", t, f);
}

字符类型

1
2
3
4
5
6
7
8
9
fn main() {
// char 类型占 4 字节,表示一个 Unicode 标量值
let c: char = 'A';
let emoji: char = '🦀'; // 支持 emoji
let zh: char = '中'; // 支持中文

println!("c={}, emoji={}, zh={}", c, emoji, zh);
println!("char 占用 {} 字节", std::mem::size_of::<char>()); // 输出 4
}

2. 复合类型(Compound Types)

元组(Tuple)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
// 元组可以包含不同类型,长度固定
let tup: (i32, f64, char) = (500, 6.4, 'R');

// 模式匹配解构
let (x, y, z) = tup;
println!("x={}, y={}, z={}", x, y, z); // 输出 x=500, y=6.4, z=R

// 使用点索引访问(从 0 开始)
println!("第一个: {}", tup.0); // 输出 500
println!("第二个: {}", tup.1); // 输出 6.4
println!("第三个: {}", tup.2); // 输出 R

// 空元组 () 称为单元类型,用于不返回值的表达式
let empty: () = ();
}

数组(Array)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
// 数组长度固定,所有元素类型相同,分配在栈上
let arr: [i32; 5] = [1, 2, 3, 4, 5];

// 简写:创建 3 个值为 0 的数组
let zeros = [0; 3]; // [0, 0, 0]

// 索引访问(从 0 开始)
println!("第一个: {}", arr[0]); // 输出 1
println!("数组长度: {}", arr.len()); // 输出 5

// 数组越界会在运行时 panic(程序崩溃)
// let x = arr[10]; // ❌ index out of bounds

// 遍历数组
for element in arr.iter() {
println!("元素: {}", element);
}

// 带索引遍历
for (i, val) in arr.iter().enumerate() {
println!("arr[{}] = {}", i, val);
}
}

函数与作用域

Rust 使用 fn 关键字定义函数,函数名遵循蛇形命名法(snake_case)。main 函数是程序的入口点。

1. 函数定义与调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// 调用自定义函数
greet();
let result = add(10, 20);
println!("10 + 20 = {}", result);
}

// 无参无返回值函数
fn greet() {
println!("Hello, Rust!");
}

// 带参数和返回值的函数(-> 标注返回类型)
fn add(a: i32, b: i32) -> i32 {
// 最后一行不带分号即为返回值(表达式返回)
a + b // 返回 a + b
}

2. 语句与表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
// 语句:执行操作但不返回值,以分号结尾
let x = 5; // let 语句

// 表达式:计算并返回值,不以分号结尾
let y = {
let a = 3;
a + 1 // 表达式,返回 4
};
println!("y = {}", y); // 输出 y = 4

// 函数体内最后的表达式即为返回值
let z = double(5);
println!("double(5) = {}", z); // 输出 10
}

fn double(n: i32) -> i32 {
n * 2 // 表达式返回,不能加分号
}

3. 作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
let outer = 100; // outer 在整个 main 中有效

{
// 进入一个新的作用域
let inner = 200; // inner 仅在此作用域有效
println!("内部: outer={}, inner={}", outer, inner);
// inner 在此处之后被释放(drop)
}

println!("外部: outer={}", outer);
// println!("{}", inner); // ❌ 编译错误!inner 已超出作用域
}

流程控制

Rust 通过 ifloopwhilefor 来控制代码的执行流程。

1. if 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let number = 7;

// 基本 if-else
if number < 5 {
println!("数字小于 5");
} else if number == 5 {
println!("数字等于 5");
} else {
println!("数字大于 5"); // 走这里
}

// if 是表达式,可以用于赋值(各分支必须返回相同类型)
let result = if number % 2 == 0 { "偶数" } else { "奇数" };
println!("{} 是{}", number, result); // 输出 7 是奇数

// 条件必须是 bool 类型,不支持隐式转换
// if number { } // ❌ number 不是 bool
}

2. loop 循环

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 count = 0;

// loop 无限循环,使用 break 退出
loop {
count += 1;
println!("count = {}", count);

if count >= 3 {
break; // 退出循环
}
}

// loop 可以返回值
let result = loop {
count += 1;
if count == 10 {
break count * 2; // break 后跟返回值
}
};
println!("result = {}", result); // 输出 result = 20
}

3. while 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
let mut n = 3;

// 条件循环
while n > 0 {
println!("倒计时: {}", n);
n -= 1;
}
println!("发射!🚀");

// 遍历数组(用 while 不如用 for 方便)
let arr = [10, 20, 30, 40, 50];
let mut index = 0;
while index < arr.len() {
println!("arr[{}] = {}", index, arr[index]);
index += 1;
}
}

4. for 循环

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 = [10, 20, 30, 40, 50];

// for in 遍历集合(推荐方式,安全高效)
for element in arr.iter() {
println!("元素: {}", element);
}

// 遍历范围(左闭右开)
for i in 0..5 {
println!("i = {}", i); // 输出 0, 1, 2, 3, 4
}

// 遍历范围(左闭右闭)
for i in 1..=3 {
println!("i = {}", i); // 输出 1, 2, 3
}

// 反向遍历
for i in (1..=5).rev() {
println!("倒序: {}", i); // 输出 5, 4, 3, 2, 1
}
}

所有权与借用(Ownership and Borrowing)

所有权是 Rust 最独特的特性,它让 Rust 无需垃圾回收器就能保证内存安全。所有权规则:每个值有且只有一个所有者;值在所有者离开作用域时被释放。

1. 所有权规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
// 规则一:每个值都有一个所有者
let s1 = String::from("hello");
println!("s1 = {}", s1);
// s1 是 "hello" 这个 String 的所有者

// 规则二:同一时刻只能有一个所有者
let s2 = s1; // s1 的所有权移动(move)给了 s2
// println!("{}", s1); // ❌ s1 已失效!
println!("s2 = {}", s2);

// 规则三:所有者离开作用域时,值被释放
{
let s3 = String::from("temp");
println!("内部: {}", s3);
// s3 在此被 drop,内存释放
}
// println!("{}", s3); // ❌ s3 已超出作用域
}

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
29
30
31
32
33
34
35
36
fn main() {
let s = String::from("hello");

// 将 s 传入函数,所有权随之移动
takes_ownership(s);
// println!("{}", s); // ❌ s 的所有权已移入函数

// 基本类型(实现了 Copy trait)会自动复制
let x = 5;
makes_copy(x); // x 被复制,所有权不变
println!("x 仍然可用: {}", x); // ✅

// 函数返回时也可以转移所有权
let s1 = gives_ownership(); // 函数返回值所有权移给 s1
let s2 = String::from("world");
let s3 = takes_and_gives_back(s2); // s2 移入,s3 获得返回值的所有权
println!("s1={}, s3={}", s1, s3);
}

fn takes_ownership(s: String) {
println!("获取了所有权: {}", s);
// s 离开作用域,内存释放
}

fn makes_copy(n: i32) {
println!("复制了: {}", n);
}

fn gives_ownership() -> String {
let s = String::from("from function");
s // 返回 s,所有权移出
}

fn takes_and_gives_back(s: String) -> String {
s // 返回 s,所有权移出
}

3. 借用(Borrowing)

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
fn main() {
let s = String::from("hello");

// 不可变借用:使用 & 创建引用
let len = calculate_length(&s); // &s 创建一个指向 s 的引用
// 借用不会转移所有权
println!("'{}' 的长度是 {}", s, len); // ✅ s 仍然可用

// 可变借用
let mut s2 = String::from("hello");
change(&mut s2); // &mut 创建可变引用
println!("修改后: {}", s2); // 输出 hello, world!

// 借用规则:
// 1. 同一时刻,要么多个不可变引用,要么一个可变引用
// 2. 引用必须始终有效(不会悬垂)
let r1 = &s2; // ✅ 不可变引用
let r2 = &s2; // ✅ 可以有多个不可变引用
// let r3 = &mut s2; // ❌ 已有不可变引用时不能创建可变引用
println!("r1={}, r2={}", r1, r2);
// r1, r2 在此之后不再使用

let r3 = &mut s2; // ✅ 现在可以了(r1, r2 已不再使用)
r3.push_str("!!!");
println!("r3={}", r3);
// 这个作用域称为 NLL(Non-Lexical Lifetimes)
}

fn calculate_length(s: &String) -> usize { // &String 表示借用
s.len()
// s 是引用,离开作用域时不会释放所指数据
}

fn change(s: &mut String) {
s.push_str(", world!"); // 通过可变引用修改原数据
}

引用与切片(References and Slices)

切片(Slice)是对集合中连续元素的引用,不拥有数据。最常见的切片类型是字符串切片 &str 和数组切片 &[T]

1. 字符串切片

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
fn main() {
let s = String::from("hello world");

// 字符串切片 [起始..结束) ,左闭右开
let hello = &s[0..5]; // 取 0 到 4 索引的字符
let world = &s[6..11]; // 取 6 到 10 索引的字符
println!("{}, {}!", hello, world); // 输出 hello, world!

// 简写
let h = &s[..5]; // 从开头到索引 5(不含)
let w = &s[6..]; // 从索引 6 到结尾
let all = &s[..]; // 整个字符串切片
println!("h={}, w={}, all={}", h, w, all);

// 推荐函数参数使用 &str 而非 &String
let first = first_word(&s);
println!("第一个单词: {}", first); // 输出 hello

// 字符串字面量本身就是切片
let literal = "hello world"; // literal 的类型是 &str
let first2 = first_word(literal);
println!("第一个单词: {}", first2);

// 注意:切片引用与原始数据的关联
// let mut s2 = String::from("hello");
// let slice = &s2[..]; // 不可变借用
// s2.clear(); // ❌ 可变借用与不可变借用冲突!
// println!("{}", slice);
}

// 使用 &str 作为参数,可同时接受 &String 和 &str
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i]; // 返回第一个空格之前的切片
}
}
&s[..] // 没有空格,返回整个字符串
}

2. 数组切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
let arr = [1, 2, 3, 4, 5];

// 数组切片类型是 &[i32]
let slice: &[i32] = &arr[1..4]; // 取索引 1, 2, 3
println!("切片: {:?}", slice); // 输出 [2, 3, 4]

// 切片和原数组共享数据
println!("第一个元素: {}", slice[0]); // 输出 2
println!("切片长度: {}", slice.len()); // 输出 3

// 判断切片是否为空
let empty: &[i32] = &[];
println!("empty.is_empty() = {}", empty.is_empty()); // 输出 true

// 遍历切片
for val in slice.iter() {
println!("元素: {}", val);
}
}

结构体(Struct)和方法

结构体是一种自定义数据类型,用于将多个相关的值组合成一个有意义的整体。可以为其定义方法,方法定义在 impl 块中。

1. 定义与实例化结构体

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 User {
username: String,
email: String,
active: bool,
}

fn main() {
// 创建实例(字段必须全部初始化)
let user1 = User {
email: String::from("alice@example.com"),
username: String::from("alice"),
active: true,
};
println!("用户名: {}, 邮箱: {}", user1.username, user1.email);

// 如果实例声明为 mut,所有字段都可变
let mut user2 = User {
email: String::from("bob@example.com"),
username: String::from("bob"),
active: false,
};
user2.active = true; // ✅ 修改字段
println!("{} 激活状态: {}", user2.username, user2.active);

// 结构体更新语法(类似 JS 展开运算符)
let user3 = User {
email: String::from("charlie@example.com"),
..user2 // 其余字段从 user2 获取(注意:user2.username 的所有权被移走了)
};
println!("新用户: {}, {}", user3.username, user3.email);
// println!("{}", user2.username); // ❌ user2.username 已失效
}

2. 元组结构体和单元结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 元组结构体:有结构体名但没有字段名
struct Color(i32, i32, i32);
struct Point(f64, f64);

// 单元结构体:没有任何字段
struct AlwaysEqual;

fn main() {
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);

// 通过索引访问元组结构体字段
println!("R: {}, G: {}, B: {}", black.0, black.1, black.2);
println!("x: {}, y: {}", origin.0, origin.1);

let _subject = AlwaysEqual; // 单元结构体实例
// 单元结构体常用于实现 trait 但不需要存储数据
}

3. 方法定义(impl 块)

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
struct Rectangle {
width: u32,
height: u32,
}

// impl 块中定义方法
impl Rectangle {
// 关联函数(没有 self 参数)—— 相当于静态方法
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}

// 方法:第一个参数是 &self(不可变借用)
fn area(&self) -> u32 {
self.width * self.height
}

// &mut self:可变借用
fn double_size(&mut self) {
self.width *= 2;
self.height *= 2;
}

// self:获取所有权(较少使用)
fn destroy(self) -> (u32, u32) {
(self.width, self.height)
}

// 可以调用其他方法
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}

fn main() {
let mut rect = Rectangle {
width: 30,
height: 50,
};

// 调用方法(自动解引用)
println!("面积: {} 平方像素", rect.area()); // 输出 1500

// 调用可变借用方法
rect.double_size();
println!("放大后: {} x {}", rect.width, rect.height); // 输出 60 x 100

// 调用关联函数:使用 :: 语法
let sq = Rectangle::square(10);
println!("正方形面积: {}", sq.area()); // 输出 100

// 比较矩形
let small = Rectangle { width: 5, height: 5 };
println!("rect 能容纳 small 吗?{}", rect.can_hold(&small));
}

枚举与模式匹配

枚举(enum)定义了一组可能的取值类型。结合 match 模式匹配,Rust 能安全地处理所有情况,编译器会检查是否覆盖了所有分支。

1. 枚举定义与使用

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
// 基本枚举
#[derive(Debug)] // 自动实现 Debug trait,使枚举可打印
enum IpAddrKind {
V4,
V6,
}

// 带数据的枚举
#[derive(Debug)]
enum IpAddr {
V4(u8, u8, u8, u8), // 关联数据
V6(String),
}

// 枚举也可以定义方法
impl IpAddr {
fn describe(&self) -> String {
match self {
IpAddr::V4(a, b, c, d) => format!("IPv4: {}.{}.{}.{}", a, b, c, d),
IpAddr::V6(addr) => format!("IPv6: {}", addr),
}
}
}

fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
println!("{:?}, {:?}", four, six);

let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
println!("{:?}", home);
println!("{}", home.describe()); // 输出 IPv4: 127.0.0.1
println!("{}", loopback.describe()); // 输出 IPv6: ::1
}

2. Option 枚举(替代空值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// Option<T> 是标准库定义的枚举,用于表示可能有值或没有值
// enum Option<T> { Some(T), None }
// Option 和其变体都被自动引入作用域

let some_number = Some(5); // Option<i32>
let some_string = Some("hello"); // Option<&str>
let absent: Option<i32> = None; // 必须标注类型

// 使用 Option 值前需要处理 None 的情况
let x = 5;
let y = Some(10);

// 不能直接运算:x + y // ❌ 类型不匹配 i32 + Option<i32>
let sum = x + y.unwrap_or(0); // 使用 unwrap_or 提供默认值
println!("sum = {}", sum); // 输出 15
}

3. match 模式匹配

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
#[derive(Debug)]
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}

// 带数据的枚举变体
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}

fn main() {
// match 必须覆盖所有可能的分支
let coin = Coin::Dime;
let value = value_in_cents(coin);
println!("价值: {} 美分", value);

// 使用 match 处理 Option
let opt_val = Some(3);
let result = plus_one(opt_val);
println!("{:?}", result); // 输出 Some(4)

let none_val = plus_one(None);
println!("{:?}", none_val); // 输出 None

// 匹配带数据的枚举
let msg = Message::Write(String::from("hello"));
process_message(msg);

let msg2 = Message::Move { x: 10, y: 20 };
process_message(msg2);
}

fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("幸运便士!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}

fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}

fn process_message(msg: Message) {
match msg {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("写入: {}", text),
Message::ChangeColor(r, g, b) => println!("改为颜色 ({}, {}, {})", r, g, b),
}
}

4. if let 简洁控制流

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 config_max = Some(3u8);

// 使用 match(啰嗦但完整)
match config_max {
Some(max) => println!("最大值配置为 {}", max),
_ => (), // 忽略其他变体
}

// 等价于 if let(简洁,但失去穷尽性检查)
if let Some(max) = config_max {
println!("最大值配置为 {}", max);
}

// if let 也可以配合 else
let coin = Some(5);
if let Some(value) = coin {
println!("硬币价值: {}", value);
} else {
println!("没有硬币");
}
}

常见集合类型及常用操作

Rust 标准库提供了几种通用的集合类型:Vec<T>(动态数组)、String(字符串)、HashMap<K,V>(哈希映射)。

1. Vec<T> 动态数组

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
fn main() {
// 创建向量
let mut v1: Vec<i32> = Vec::new();
let mut v2 = vec![1, 2, 3]; // vec! 宏创建

// 添加元素
v1.push(10);
v1.push(20);
v1.push(30);

// 读取元素
println!("v1[0] = {}", v1[0]); // 索引访问(越界会 panic)
println!("v1.get(1) = {:?}", v1.get(1)); // get 返回 Option,安全
println!("v1.get(100) = {:?}", v1.get(100)); // 返回 None

// 遍历向量
for val in &v2 {
println!("元素: {}", val);
}

// 遍历并修改
for val in &mut v2 {
*val *= 2; // * 解引用
}
println!("翻倍后: {:?}", v2); // 输出 [2, 4, 6]

// 常用操作
println!("长度: {}", v2.len()); // 输出 3
println!("是否为空: {}", v2.is_empty()); // 输出 false
v2.pop(); // 移除最后一个元素,返回 Option<T>
println!("pop 后: {:?}", v2); // 输出 [2, 4]

// 枚举类型可以在向量中存储不同类型(通过枚举统一类型)
#[derive(Debug)]
enum Cell {
Int(i32),
Text(String),
Float(f64),
}
let row = vec![
Cell::Int(42),
Cell::Text(String::from("hello")),
Cell::Float(3.14),
];
println!("行数据: {:?}", row);
}

2. 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
fn main() {
// 创建字符串
let mut s1 = String::new();
let mut s2 = String::from("hello"); // 从 &str 创建
let s3 = "world".to_string(); // to_string 方法

// 追加内容
s1.push_str("rust"); // 追加 &str(不获取所有权)
s2.push(' '); // 追加单个字符
s2.push_str("world");

// 拼接
let joined = format!("{}-{}-{}", s1, s2, s3); // format! 宏(不获取所有权)
println!("拼接结果: {}", joined);

// 使用 + 运算符拼接(会获取第一个字符串的所有权)
let combined = s1 + " & " + &s3;
println!("combined: {}", combined);
// println!("{}", s1); // ❌ s1 已被移动

// 遍历字符
for c in "你好".chars() {
println!("字符: {}", c);
}

// 遍历字节
for b in "ABC".bytes() {
println!("字节: {}", b);
}

// 索引与切片(需注意 UTF-8 边界)
let hello = String::from("你好世界");
let slice = &hello[0..3]; // 一个中文字符占 3 字节(UTF-8)
println!("切片: {}", slice); // 输出 你

// 常用方法
let s = String::from("hello world");
println!("长度(字节): {}", s.len()); // 输出 11
println!("是否为空: {}", s.is_empty()); // 输出 false
println!("包含'world': {}", s.contains("world")); // 输出 true
println!("替换: {}", s.replace("world", "rust")); // 输出 hello rust
let words: Vec<&str> = s.split(' ').collect(); // 分割
println!("拆分结果: {:?}", words); // 输出 ["hello", "world"]
println!("转大写: {}", s.to_uppercase()); // 输出 HELLO WORLD
println!("去除空白: {}", " abc ".trim()); // 输出 abc
}

3. HashMap<K, 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
35
36
37
38
39
40
41
42
43
use std::collections::HashMap;

fn main() {
// 创建 HashMap
let mut scores = HashMap::new();

// 插入键值对
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

// 访问值(get 返回 Option<&V>)
let team = String::from("Blue");
match scores.get(&team) {
Some(score) => println!("Blue 队分数: {}", score),
None => println!("Blue 队不存在"),
}

// 遍历
for (key, value) in &scores {
println!("{}: {}", key, value);
}

// 只在键不存在时插入
scores.entry(String::from("Blue")).or_insert(100); // Blue 已存在,不更新
scores.entry(String::from("Red")).or_insert(30); // Red 不存在,插入 30
println!("插入后: {:?}", scores);

// 基于旧值更新
let text = "hello world hello";
let mut word_count = HashMap::new();
for word in text.split_whitespace() {
let count = word_count.entry(word).or_insert(0);
*count += 1; // 解引用修改
}
println!("词频统计: {:?}", word_count); // 输出 {"hello": 2, "world": 1}

// 从 Vec 创建 HashMap
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let team_scores: HashMap<_, _> =
teams.into_iter().zip(initial_scores.into_iter()).collect();
println!("聚合结果: {:?}", team_scores);
}

模块系统和包管理

Rust 的模块系统由四个核心概念组成:包(Package)、Crate(库/二进制)、模块(Module)、路径(Path)。use 关键字用于将路径引入作用域。

1. 模块定义与可见性

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
// 定义模块树
mod front_of_house {
// 默认模块内容是私有的
pub mod hosting { // pub 使模块公开
pub fn add_to_waitlist() { // pub 使函数公开
println!("已添加到等候名单");
}

fn seat_at_table() { // 私有函数,仅在模块内部可用
println!("请就座");
}
}

mod serving {
fn take_order() {
println!("点菜");
// super 引用父模块
super::hosting::add_to_waitlist();
}
}
}

// 使用 pub use 重新导出(re-export)
pub use front_of_house::hosting;

// 使用 use 引入路径
use front_of_house::hosting::add_to_waitlist;

fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();

// 相对路径
front_of_house::hosting::add_to_waitlist();

// 使用 use 引入后可直接调用
add_to_waitlist();
}

fn main() {
eat_at_restaurant();
}

2. 多文件模块结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 在实际项目中,模块通常会拆分到不同文件中:
//
// src/main.rs
// mod garden; // 告诉编译器在 src/garden.rs 或 src/garden/mod.rs 中查找模块
//
// src/garden.rs (或 src/garden/mod.rs)
// pub mod vegetables; // 在 src/garden/vegetables.rs 中
//
// 旧式路径:src/garden/vegetables.rs
// 新式路径:src/garden/vegetables/mod.rs

// 以一个简化示例演示 mod 的机制:
mod utils {
pub fn greet(name: &str) {
println!("你好,{}!", name);
}
}

fn main() {
utils::greet("Rustacean"); // 输出 你好,Rustacean!
}

3. use 关键字用法

1
2
3
4
5
6
7
8
9
10
11
12
13
use std::collections::HashMap;
use std::io::{self, Write}; // 同时引入多个(io 自身也被引入)
use std::collections::*; // glob 引入(谨慎使用)

fn main() {
let mut map = HashMap::new(); // 不用写 std::collections::HashMap
map.insert("key", "value");
println!("{:?}", map);

// io::stdout() 和 Write trait
let mut stdout = io::stdout();
writeln!(stdout, "通过 use 简化了路径!").unwrap();
}

4. Cargo 包管理

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
// Cargo.toml 文件示例(注释形式演示,不是可执行代码):
//
// [package]
// name = "my_project"
// version = "0.1.0"
// edition = "2021" # Rust 版本
//
// [dependencies]
// serde = "1.0" # 添加依赖
// rand = { version = "0.8", features = ["small_rng"] } # 指定版本和 features
//
// [dev-dependencies]
// criterion = "0.5" # 仅用于测试/基准测试的依赖
//
// 常用 Cargo 命令:
// cargo new project_name # 创建新项目
// cargo build # 编译项目
// cargo build --release # 编译发布版本(优化)
// cargo run # 编译并运行
// cargo test # 运行测试
// cargo check # 检查代码能否编译(比 build 快)
// cargo doc --open # 生成并打开文档
// cargo update # 更新依赖
// cargo add <crate> # 添加依赖(需安装 cargo-edit)

fn main() {
println!("Cargo 是 Rust 的构建系统和包管理器!");
}

错误处理

Rust 将错误分为两类:可恢复错误(Result<T, E>)和不可恢复错误(panic!)。推荐使用 Result 进行错误传播。

1. panic! 宏(不可恢复错误)

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
// panic! 会立即终止程序并展开栈(unwinding)
// panic!("程序崩溃了!"); // 取消注释试试

// 直接设置环境变量可获取更详细的回溯:
// $env:RUST_BACKTRACE=1 (PowerShell)
// export RUST_BACKTRACE=1 (bash)

let v = vec![1, 2, 3];
// v[99]; // ❌ 索引越界,触发 panic
println!("v 有效: {:?}", v);
}

2. Result<T, E>(可恢复错误)

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
use std::fs::File;
use std::io::{self, Read, ErrorKind};

fn main() {
// 方式一:使用 match 处理 Result
let file_result = File::open("hello.txt");
let file = match file_result {
Ok(f) => f,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("无法创建文件: {:?}", e),
},
other_error => panic!("打开文件出错: {:?}", other_error),
},
};
println!("文件打开成功: {:?}", file.metadata());

// 方式二:使用闭包和 unwrap_or_else(更简洁)
let _file2 = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("无法创建文件: {:?}", error);
})
} else {
panic!("打开文件出错: {:?}", error);
}
});
}

// unwrap 和 expect
fn demo_unwrap() {
// unwrap:成功返回值,失败直接 panic
let _f = File::open("hello.txt").unwrap();

// expect:类似 unwrap,但可自定义 panic 消息(推荐用于原型开发)
let _f = File::open("hello.txt").expect("无法打开 hello.txt");
}

3. 错误传播(? 操作符)

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
use std::fs::{self, File};
use std::io::{self, Read};

// 传统方式:match 逐层传播错误
fn read_username_from_file_v1() -> Result<String, io::Error> {
let f = File::open("username.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e), // 遇到错误提前返回
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}

// 使用 ? 操作符:遇到错误立即返回 Err(简洁!)
fn read_username_from_file_v2() -> Result<String, io::Error> {
let mut f = File::open("username.txt")?; // ? = match + return Err
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}

// ? 可以链式调用,进一步简化
fn read_username_from_file_v3() -> Result<String, io::Error> {
let mut s = String::new();
File::open("username.txt")?.read_to_string(&mut s)?;
Ok(s)
}

// 最简版本:使用标准库快捷函数
fn read_username_from_file_v4() -> Result<String, io::Error> {
fs::read_to_string("username.txt") // std::fs::read_to_string 一步完成
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
// main 也可以返回 Result
match read_username_from_file_v4() {
Ok(name) => println!("用户名: {}", name),
Err(e) => eprintln!("读取错误: {}", e),
}
Ok(())
}

泛型(Generics)

泛型允许编写适用于多种类型的代码,减少重复。Rust 的泛型在编译时通过单态化(monomorphization)实现零成本抽象——为每个使用的具体类型生成专用代码。

1. 函数中的泛型

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 largest_i32(list: &[i32]) -> &i32 {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}

// 使用泛型(需要 PartialOrd trait bound,见下一章 Traits)
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}

fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("最大数: {}", largest(&numbers)); // 输出 100

let chars = vec!['y', 'm', 'a', 'q'];
println!("最大字符: {}", largest(&chars)); // 输出 y
}

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 单泛型参数
#[derive(Debug)]
struct Point<T> {
x: T,
y: T, // x 和 y 必须同类型
}

// 多泛型参数
#[derive(Debug)]
struct Point2<T, U> {
x: T,
y: U, // x 和 y 可以不同类型
}

// 为泛型结构体实现方法
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}

// 只为特定类型实现方法
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}

impl<T: Clone, U> Point2<T, U> {
fn mixup<V, W>(self, other: Point2<V, W>) -> Point2<T, W> {
Point2 {
x: self.x.clone(),
y: other.y,
}
}
}

fn main() {
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
println!("int_point.x = {}", integer_point.x());
println!("float_point 到原点距离 = {}", float_point.distance_from_origin());

let p1 = Point2 { x: 5, y: "hello" };
let p2 = Point2 { x: "world", y: 'R' };
let p3 = p1.mixup(p2);
println!("mixup: {:?}", p3); // Point2 { x: 5, y: 'R' }

// 不允许不同类型:Point { x: 5, y: 4.0 }; // ❌
// 需要不同类型用 Point2
let _flexible = Point2 { x: 5, y: 4.0 };
}

3. 枚举中的泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 标准库中的 Option 和 Result 就是泛型枚举
// enum Option<T> { Some(T), None }
// enum Result<T, E> { Ok(T), Err(E) }

#[derive(Debug)]
enum MyResult<T, E> {
Success(T),
Failure(E),
}

fn main() {
let ok: MyResult<i32, String> = MyResult::Success(42);
let err: MyResult<i32, String> = MyResult::Failure(String::from("出错了"));
println!("{:?}, {:?}", ok, err);

// 使用 Option 和 Result
let x: Option<i32> = Some(10);
let y: Result<i32, &str> = Ok(20);
println!("{:?}, {:?}", x, y);
}

Trait 与 Trait Bound

Trait 定义了类型之间共享的行为,类似于其他语言中的接口(interface)。Trait Bound 用于约束泛型参数必须实现某些 trait。

1. 定义和实现 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
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
// 定义 trait
pub trait Summary {
// 方法签名(无默认实现)
fn summarize_author(&self) -> String;

// 带默认实现的方法
fn summarize(&self) -> String {
// 默认实现可以调用同一 trait 中的其他方法
format!("(阅读更多来自 {} 的内容...)", self.summarize_author())
}
}

// 定义一个结构体
pub struct Article {
pub headline: String,
pub author: String,
pub content: String,
}

pub struct Tweet {
pub username: String,
pub content: String,
pub retweet: bool,
}

// 为 Article 实现 Summary
impl Summary for Article {
fn summarize_author(&self) -> String {
format!("@{}", self.author)
}
// summarize 使用默认实现
}

// 为 Tweet 实现 Summary(覆盖默认实现)
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}

fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}

fn main() {
let article = Article {
headline: String::from("Rust 很强大"),
author: String::from("Ferris"),
content: String::from("今天我们来学习 Rust..."),
};

let tweet = Tweet {
username: String::from("ferris_rustacean"),
content: String::from("Rust is awesome!"),
retweet: false,
};

println!("文章摘要: {}", article.summarize());
println!("推文摘要: {}", tweet.summarize());
}

2. Trait Bound(约束)

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
use std::fmt::{Debug, Display};

// 使用 trait bound 约束泛型参数
pub fn notify(item: &impl Summary) {
// impl Summary 表示参数必须实现了 Summary trait
println!("快讯!{}", item.summarize());
}

// 等价写法(trait bound 语法,更灵活)
pub fn notify_bound<T: Summary>(item: &T) {
println!("快讯!{}", item.summarize());
}

// 多个 trait bound
pub fn notify_and_debug(item: &(impl Summary + Display)) {}

// 等价的 where 子句(约束多时推荐使用,可读性好)
pub fn some_function<T, U>(t: &T, u: &U) -> String
where
T: Display + Clone,
U: Clone + Debug,
{
format!("t={}, u={:?}", t, u)
}

// 返回实现了 trait 的类型
pub fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("ferris"),
content: String::from("返回 impl Trait"),
retweet: false,
}
// 注意:只能返回一种具体类型,不能有条件地返回不同类型
}

fn main() {
let tweet = returns_summarizable();
println!("{}", tweet.summarize());

let s1 = String::from("hello");
let s2 = String::from("world");
println!("{}", some_function(&s1, &s2));
}

3. 派生 Trait(Derive)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 通过 #[derive] 属性自动实现常用 trait
#[derive(Debug, Clone, PartialEq, Eq, Hash)] // Debug: 打印调试信息
struct Person { // Clone: 显式克隆
name: String, // PartialEq/Eq: 相等比较
age: u32, // Hash: 哈希
}

fn main() {
let p1 = Person { name: String::from("Alice"), age: 30 };
let p2 = p1.clone(); // Clone trait

println!("{:?}", p1); // Debug trait
println!("p1 == p2: {}", p1 == p2); // PartialEq trait
}

4. Orphan Rule(孤儿规则)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 孤儿规则:只能为当前 crate 中的类型实现外部 trait,
// 或者为外部类型实现当前 crate 中的 trait。
// 不能为外部类型实现外部 trait(避免冲突)。

use std::fmt::Display;

// 本地类型 + 外部 trait = ✅ 允许
struct MyVec(Vec<i32>);
impl Display for MyVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MyVec with {} elements", self.0.len())
}
}

fn main() {
let mv = MyVec(vec![1, 2, 3]);
println!("{}", mv);
// 但不能 impl Display for Vec<i32> // ❌ 外部类型 + 外部 trait
}

生命周期(Lifetimes)

生命周期是 Rust 用来防止悬垂引用的机制。生命周期注解描述了引用之间存活时间的关系,大多数情况下编译器可自动推断,复杂场景需要手动标注。

1. 生命周期基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// 编译器通过生命周期分析防止悬垂引用
{
let x = 5;
let r = &x; // r 的生命周期不能超过 x
println!("r: {}", r);
}
// r 在此已失效,x 也已失效

// 下面的代码不能通过编译:
// let r;
// {
// let x = 5;
// r = &x; // ❌ x 活得不够久
// }
// println!("r: {}", r); // r 引用了已释放的 x
}

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
29
30
31
32
// 生命周期注解语法:&'a T
// 'a 是一种命名约定,读作"生命周期 a"

// 编译器的借用检查器无法自动推断此函数的生命周期
// 需要手动标注输入和输出引用之间的关系
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
// 'a 表示:返回引用的生命周期等于 x 和 y 中较短的那个
if x.len() > y.len() {
x
} else {
y
}
}

fn main() {
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(&string1, &string2);
println!("最长的字符串是: {}", result); // ✅ result 的生命周期与 string2 一致
}
// string2 已释放,result 也随之失效

// 下面的代码不能通过编译:
// let result;
// let string1 = String::from("long");
// {
// let string2 = String::from("xyz");
// result = longest(&string1, &string2); // ❌ result 比 string2 活得久
// }
// println!("{}", result);
}

3. 结构体中的生命周期

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
// 当结构体持有引用时,需要标注生命周期
#[derive(Debug)]
struct Excerpt<'a> {
// 'a 表示 part 引用的数据必须比 Excerpt 实例活得久
part: &'a str,
}

impl<'a> Excerpt<'a> {
// 方法中生命周期省略规则通常可以自动推导,但此处需要显式标注
fn level(&self) -> i32 {
3
}

fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("注意: {}", announcement);
self.part // 根据省略规则,返回值生命周期与 &self 一致
}
}

fn main() {
let novel = String::from("从前有座山,山里有座庙...");
let first_sentence = novel.split('。').next().expect("找不到句号");
let excerpt = Excerpt { part: first_sentence };
println!("摘录: {:?}", excerpt);
println!("返回: {}", excerpt.announce_and_return_part("请注意"));
}

4. 生命周期省略规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 三条省略规则(编译器在能推断时会自动添加):
// 1. 每个是引用的参数都有各自的生命周期注解
// 2. 如果只有一个输入生命周期参数,该生命周期赋给所有输出参数
// 3. 如果方法有多个输入生命周期参数,但其中一个是 &self 或 &mut self,
// 则 self 的生命周期赋给所有输出参数

fn first_word(s: &str) -> &str {
// 编译器根据规则 1 和 规则 2 自动推断:
// fn first_word<'a>(s: &'a str) -> &'a str
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}

fn main() {
let s = "hello world";
println!("第一个单词: {}", first_word(s));
}

5. 静态生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
// 'static 是特殊的生命周期,表示引用在整个程序运行期间都有效
// 所有字符串字面量都是 'static 生命周期

fn main() {
let s: &'static str = "我是静态字符串,永远有效";
println!("{}", s);

// 常量引用的数据通常是 'static
let pi: &'static f64 = &3.14159;
// 实际上,上面的写法不工作,因为 3.14159 是临时值。正确的写法:
// 要么用 const,要么直接用字面量
println!("pi 近似值: {}", pi);
}

常用标准库函数与实用宏

Rust 标准库和内置宏提供了丰富的工具函数,掌握它们能大幅提升开发效率。

1. 常用 println! / format! 系列宏

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 name = "Ferris";
let age = 5;

// 基本占位符
println!("你好,{}!", name);

// 多个占位符 + 位置参数
println!("{0} 今年 {1} 岁,{0} 喜欢 Rust!", name, age);

// 命名参数
println!("{name} 写了 {count} 行代码", name = "Alice", count = 1000);

// 调试打印
let arr = vec![1, 2, 3];
println!("数组: {:?}", arr); // Debug 格式
println!("美化: {:#?}", arr); // 带缩进的 Debug 格式

// 数字格式化
let num = 255;
println!("十进制: {}, 十六进制: {:x}, 八进制: {:o}, 二进制: {:b}", num, num, num, num);
println!("前导零: {:08}", 42); // 输出 00000042
println!("精度: {:.3}", 3.14159); // 输出 3.142

// 不换行和错误输出
print!("不换行...");
eprintln!("这是错误输出!"); // 输出到 stderr

// format! 返回 String 而非打印
let msg = format!("{} + {} = {}", 1, 2, 3);
println!("{}", msg);
}

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
fn main() {
// vec! —— 创建 Vec
let v = vec![1, 2, 3, 4, 5];
println!("{:?}", v);

// assert! / assert_eq! / assert_ne! —— 断言
assert!(1 + 1 == 2, "数学失效了!");
assert_eq!(2 + 2, 4); // 相等断言
assert_ne!(2 + 2, 5); // 不等断言

// dbg! —— 快速调试(打印表达式与值,返回所有权)
let x = dbg!(5 * 10); // 打印: [file:line] 5 * 10 = 50

// todo! —— 标记未完成的代码(会触发 panic,但不阻碍编译)
// let unfinished = todo!("等会儿再实现");

// unimplemented! —— 标记未实现
// let not_done = unimplemented!("这个功能还没做");

// unreachable! —— 标记在逻辑上不可达的代码
// unreachable!("这里按理说永远执行不到");

// panic! —— 主动触发 panic
// panic!("手动触发崩溃");

// matches! —— 匹配宏,返回 bool
let opt = Some(42);
if matches!(opt, Some(x) if x > 0) {
println!("大于零的值!");
}

// include_str! —— 编译时嵌入文件内容为 &str
// let content = include_str!("./data.txt");

// concat! —— 编译时拼接字符串字面量
const GREETING: &str = concat!("Hello", ", ", "World!");
println!("{}", GREETING);

// env! / option_env! —— 编译时获取环境变量
// const HOME: &str = env!("HOME"); // 不存在则编译失败
// const CONFIG: Option<&str> = option_env!("CONFIG_PATH"); // 不存在为 None

println!("所有宏演示完成!");
}

3. 常用标准库函数

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
use std::cmp;

fn main() {
// === 字符串操作 ===
let s = "hello world";
println!("是否包含: {}", s.contains("world"));
println!("是否以...开头: {}", s.starts_with("he"));
println!("是否以...结尾: {}", s.ends_with("ld"));
println!("替换: {}", s.replace("world", "Rust"));
println!("查找位置: {:?}", s.find("o")); // Some(4)
let parts: Vec<&str> = s.split_whitespace().collect();
println!("按空白分割: {:?}", parts);

// === 数字操作 ===
println!("绝对值: {}", (-5i32).abs());
println!("次方: {}", 2i32.pow(10)); // 1024
println!("最大值: {}", cmp::max(10, 20));
println!("最小值: {}", cmp::min(10, 20));
println!("范围限制: {}", 100i32.clamp(0, 50)); // 50
println!("是否为 2 的幂: {}", 64u32.is_power_of_two()); // true

// === 浮点数专属 ===
let f = 3.14159_f64;
println!("四舍五入: {}", f.round()); // 3
println!("向上取整: {}", f.ceil()); // 4
println!("向下取整: {}", f.floor()); // 3
println!("截断: {}", f.trunc()); // 3
println!("平方根: {}", f.sqrt());

// === Option / Result 操作 ===
let opt_val = Some(3);
println!("unwrap_or: {}", opt_val.unwrap_or(0)); // 3
println!("map: {:?}", opt_val.map(|x| x * 2)); // Some(6)
println!("and_then: {:?}", opt_val.and_then(|x| Some(x + 1))); // Some(4)

let filtered = opt_val.filter(|&x| x > 5); // None
println!("filter: {:?}", filtered);

let result: Result<i32, &str> = Ok(42);
println!("Result unwrap_or: {}", result.unwrap_or(0));
println!("Result map: {:?}", result.map(|x| x * 2));

// === 迭代器 ===
let numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers.iter().sum(); // 15
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
let found = numbers.iter().find(|&&x| x > 3); // Some(&4)

println!("sum: {}, doubled: {:?}, evens: {:?}, found: {:?}", sum, doubled, evens, found);

// === 类型转换 ===
let num_str = "42".parse::<i32>().unwrap(); // 字符串转数字
println!("解析后的数字: {}", num_str);
let num_to_str = 42.to_string(); // 数字转字符串
println!("转换的字符串: {}", num_to_str);
let small: u8 = 10;
let big: u32 = small as u32; // 数值类型转换
println!("small as u32: {}", big);
}

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
30
31
32
33
34
35
36
37
38
39
40
use std::fs;
use std::io::{self, Write, BufRead, BufReader};
use std::path::Path;

fn main() -> io::Result<()> {
// 写入文件
fs::write("output.txt", "Hello, Rust!\n第二行内容")?;

// 追加写入
let mut file = fs::OpenOptions::new()
.append(true)
.open("output.txt")?;
writeln!(file, "追加的一行")?;

// 读取整个文件为 String
let content = fs::read_to_string("output.txt")?;
println!("文件内容:\n{}", content);

// 逐行读取
let file = fs::File::open("output.txt")?;
let reader = BufReader::new(file);
for (i, line) in reader.lines().enumerate() {
println!("第 {} 行: {}", i + 1, line?);
}

// 检查路径
let path = Path::new("output.txt");
println!("文件存在: {}", path.exists());
println!("是文件: {}", path.is_file());

// 读取目录
if Path::new(".").is_dir() {
for entry in fs::read_dir(".")? {
let entry = entry?;
println!("目录项: {:?}", entry.path());
}
}

Ok(())
}

Rust 中的异步编程(async/await)

Rust 的异步编程基于 Future trait 和 async/await 语法。异步代码不会阻塞线程,而是让出控制权等待 I/O 完成。运行时(如 tokio)负责调度执行。

1. 异步基础概念

1
2
3
4
5
6
7
8
9
// 异步编程核心概念
// - async fn:定义一个返回 Future 的异步函数
// - .await:等待一个 Future 完成,在此期间让出线程
// - 运行时(Runtime):如 tokio、async-std,负责调度和执行 Future

// 注意:以下代码需要 tokio 运行时才能运行
// 在 Cargo.toml 中添加:
// [dependencies]
// tokio = { version = "1", features = ["full"] }

2. 基本 async/await 使用

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
// 使用 tokio 运行时(注释形式演示关键概念)

// async 函数返回 impl Future<Output = T>
async fn say_hello() -> String {
String::from("Hello from async!")
}

async fn fetch_data(id: u32) -> String {
// 模拟异步 I/O 操作
// tokio::time::sleep 不会阻塞线程
// tokio::time::sleep(std::time::Duration::from_millis(100)).await;
format!("数据-{}", id)
}

// 顺序执行
async fn sequential() {
let a = fetch_data(1).await;
let b = fetch_data(2).await;
println!("顺序: a={}, b={}", a, b); // 总耗时 ~200ms
}

// 并发执行(通过 join! 宏同时等待多个 Future)
async fn concurrent() {
let (a, b) = tokio::join!(
fetch_data(1),
fetch_data(2),
);
println!("并发: a={}, b={}", a, b); // 总耗时 ~100ms
}

// #[tokio::main] 将 main 函数转为异步运行时入口
// #[tokio::main]
// async fn main() {
// say_hello().await;
// sequential().await;
// concurrent().await;
// }

// 为了不使用外部依赖也能演示语法,这里写一个概念性的 main:
fn main() {
println!("=== Rust 异步编程概念 ===");
println!("");
println!("核心概念:");
println!("1. async fn 返回一个 Future");
println!("2. .await 等待 Future 完成,线程不会被阻塞");
println!("3. tokio::spawn 可在后台并发执行任务");
println!("4. tokio::join! 同时等待多个 Future");
println!("5. tokio::select! 竞速等待,哪个先完成处理哪个");
println!("");
println!("实际使用时需添加依赖到 Cargo.toml:");
println!(" tokio = {{ version = \"1\", features = [\"full\"] }}");
}

3. 并发任务处理模式

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
// use tokio::time::{sleep, Duration};
// use std::time::Instant;

// 使用 tokio::spawn 并发执行任务
// async fn spawn_demo() {
// let handle1 = tokio::spawn(async {
// // 耗时操作1
// sleep(Duration::from_millis(100)).await;
// "任务1完成"
// });
//
// let handle2 = tokio::spawn(async {
// // 耗时操作2
// sleep(Duration::from_millis(200)).await;
// "任务2完成"
// });
//
// // 等待任务完成并获取结果
// let result1 = handle1.await.unwrap();
// let result2 = handle2.await.unwrap();
// println!("{}, {}", result1, result2);
// }

// 使用 select! 宏竞速等待
// async fn select_demo() {
// tokio::select! {
// result = fetch_data(1) => {
// println!("先完成: {}", result);
// }
// _ = sleep(Duration::from_secs(5)) => {
// println!("超时了!");
// }
// }
// }

// 使用 Stream(异步迭代器)
// use tokio_stream::StreamExt;
// async fn stream_demo() {
// // 需要 tokio-stream crate
// // let mut stream = tokio_stream::iter(vec![1, 2, 3]);
// // while let Some(val) = stream.next().await {
// // println!("stream value: {}", val);
// // }
// }

fn main() {
println!("异步并发常用模式:");
println!("- tokio::spawn: 后台并发执行任务,返回 JoinHandle");
println!("- tokio::join!: 同时等待多个 Future 完成");
println!("- tokio::select!: 竞速等待,处理最先完成的");
println!("- tokio::sync::mpsc: 异步多生产者单消费者通道");
println!("- tokio::sync::Mutex: 异步互斥锁");
println!("- Stream: 异步迭代器(需 futures 或 tokio-stream crate)");
}

4. 异步编程完整示例

// 一个完整的异步 HTTP 请求示例(概念演示)
// 实际运行需要添加 reqwest 和 tokio 依赖

// Cargo.toml:
// [dependencies]
// tokio = { version = "1", features = ["full"] }
// reqwest = { version = "0.12", features = ["json"] }
// serde = { version = "1", features = ["derive"] }

// use serde::Deserialize;
//
// #[derive(Debug, Deserialize)]
// struct Post {
//     #[serde(rename = "userId")]
//     user_id: u32,
//     id: u32,
//     title: String,
//     body: String,
// }
//
// async fn fetch_post(client: &reqwest::Client, id: u32) -> Result<Post, reqwest::Error> {
//     let url = format!("https://jsonplaceholder.typicode.com/posts/{}", id);
//     let post = client.get(&url).send().await?.json::<Post>().await?;
//     Ok(post)
// }
//
// #[tokio::main]
// async fn main() -> Result<(), Box<dyn std::error::Error>> {
//     let client = reqwest::Client::new();
//
//     // 并发获取多个 post
//     let (post1, post2, post3) = tokio::join!(
//         fetch_post(&client, 1),
//         fetch_post(&client, 2),
//         fetch_post(&client, 3),
//     );
//
//     println!("Post 1: {:?}", post1?);
//     println!("Post 2: {:?}", post2?);
//     println!("Post 3: {:?}", post3?);
//
//     Ok(())
// }

fn main() {
    println!("完整异步示例结构如上。");
    println!("");
    println!("总结——异步编程最佳实践:");
    println!("1. 选择合适的运行时(tokio 最流行)");
    println!("2. 使用 join! 并发,而不是顺序 await");
    println!("3. spawn 用于"发射后不管"的独立任务");
    println!("4. select! 用于超时控制和竞速");
    println!("5. 异步函数应该是非阻塞的(避免 CPU 密集型操作)");
    println!("6. 用 spawn_blocking 处理同步 CPU 密集型代码");
    println!("7. 使用 async Mutex/RwLock 而非 std 的同步版本");
}

**RUST Study Note 系列文章**
{% series 9 %}