Rust 编程语言中文手册

来源:https://doc.rust-lang.org/book/


目录

  1. 引言与安装
  2. 猜数字游戏实战
  3. 常见编程概念
  4. 所有权系统
  5. 结构体
  6. 枚举与模式匹配
  7. [包、Crate 与模块](#包、Crate 与模块)
  8. 常用集合
  9. 错误处理
  10. [泛型、Trait 与生命周期](#泛型、Trait 与生命周期)
  11. 编写自动化测试
  12. [I/O 项目实战](#I/O 项目实战)
  13. 函数式语言特性:闭包与迭代器
  14. [Cargo 与 Crates.io 进阶](#Cargo 与 Crates.io 进阶)
  15. 智能指针
  16. 无畏并发
  17. [Async 与 Await](#Async 与 Await)
  18. 面向对象编程特性
  19. 模式与匹配
  20. 高级特性
  21. [最终项目:构建多线程 Web 服务器](#最终项目:构建多线程 Web 服务器)

1. 引言与安装

1.1 什么是 Rust?

Rust 是一种系统编程语言,专注于三个核心目标:

  • 安全:Rust 的所有权系统在编译时保证内存安全,无需垃圾回收器
  • 速度:与 C/C++ 性能相当
  • 并发:所有权系统天然防止数据竞争

1.2 安装 Rust

推荐使用 rustup 安装:

bash 复制代码
# Linux/macOS
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

# 验证安装
rustc --version
cargo --version

更新 Rust:

bash 复制代码
rustup update

查看本地文档:

bash 复制代码
rustup doc

卸载 Rust:

bash 复制代码
rustup self uninstall

1.3 Hello, World!

rust 复制代码
fn main() {
    println!("Hello, world!");
}

编译运行:

bash 复制代码
rustc main.rs
./main        # Linux/macOS
.\main.exe    # Windows

要点:

  • fn main() 是程序的入口点
  • println! (macro),不是函数(注意 ! 后缀)
  • Rust 文件以 .rs 结尾,多个单词用下划线分隔(如 hello_world.rs
  • Rust 是预编译语言(ahead-of-time compiled),编译后的可执行文件可以分发给没有安装 Rust 的人运行

1.4 Cargo:Rust 的构建系统和包管理器

创建新项目:

bash 复制代码
cargo new hello_cargo
cd hello_cargo

项目结构:

复制代码
hello_cargo/
├── Cargo.toml    # 配置文件
├── src/
│   └── main.rs   # 源代码
└── .gitignore

Cargo.toml 示例:

toml 复制代码
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2024"

[dependencies]

常用命令:

bash 复制代码
cargo build           # 编译(debug 模式)
cargo run             # 编译并运行
cargo check           # 快速检查能否编译(不生成可执行文件)
cargo build --release # 发布模式(优化编译,运行更快)

Cargo 的优势:

  • 无论什么操作系统,命令完全一致
  • 自动管理依赖
  • 代码组织规范(源码在 src/,配置文件在根目录)
  • 构建产物在 target/ 目录

2. 猜数字游戏实战

这是一个完整的 Rust 入门项目,涵盖:变量、输入输出、循环、match、外部 crate 等核心概念。

2.1 项目设置

bash 复制代码
cargo new guessing_game
cd guessing_game

2.2 处理用户输入

rust 复制代码
use std::io;

fn main() {
    println!("猜数字游戏!");
    println!("请输入你的猜测:");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("读取行失败");

    println!("你猜的是:{guess}");
}

关键概念:

  • use std::io; --- 导入标准库的输入输出模块
  • let mut guess = String::new(); --- 创建可变变量,绑定到一个空的 String
  • String::new() --- :: 表示调用 String 类型的关联函数
  • io::stdin().read_line(&mut guess) --- 读取用户输入,&mut guess 是可变引用
  • .expect("...") --- 处理 Result 类型的错误,出错时崩溃并显示消息
  • println!("你猜的是:{guess}") --- {guess} 是直接变量插值

2.3 添加随机数生成

Cargo.toml 中添加依赖:

toml 复制代码
[dependencies]
rand = "0.8.5"

生成随机数:

rust 复制代码
use std::io;
use rand::Rng;

fn main() {
    println!("猜数字游戏!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("秘密数字已生成!");

    loop {
        println!("请输入你的猜测:");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("读取行失败");

        let guess: u32 = guess.trim().parse().expect("请输入数字!");

        println!("你猜的是:{guess}");

        match guess.cmp(&secret_number) {
            std::cmp::Ordering::Less => println!("太小了!"),
            std::cmp::Ordering::Greater => println!("太大了!"),
            std::cmp::Ordering::Equal => {
                println!("你赢了!");
                break;
            }
        }
    }
}

关键概念:

  • use rand::Rng; --- 导入随机数生成 trait
  • rand::thread_rng().gen_range(1..=100) --- 生成 1 到 100 之间的随机数
  • guess.trim().parse() --- 去除空白字符并解析为数字
  • match guess.cmp(&secret_number) --- 模式匹配比较结果
  • loop { ... break; } --- 无限循环,猜对时退出

2.4 完整的错误处理

rust 复制代码
use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
    println!("猜数字游戏!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    loop {
        println!("请输入你的猜测:");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("读取行失败");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("你猜的是:{guess}");

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("太小了!"),
            Ordering::Greater => println!("太大了!"),
            Ordering::Equal => {
                println!("你赢了!");
                break;
            }
        }
    }
}

改进点: 使用 match 替代 expect 处理解析错误,输入非数字时重新提示而不是崩溃。


3. 常见编程概念

3.1 变量与可变性

默认不可变:

rust 复制代码
let x = 5;
x = 6; // 编译错误!不能对不可变变量二次赋值

使用 mut 使变量可变:

rust 复制代码
let mut x = 5;
x = 6; // 正确

常量(const):

rust 复制代码
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
  • 必须标注类型
  • 总是不可变
  • 可以在全局作用域声明
  • 只能设置为常量表达式

变量遮蔽(Shadowing):

rust 复制代码
let x = 5;
let x = x + 1; // 遮蔽,x 现在是 6

{
    let x = x * 2; // 内部作用域遮蔽,x 是 12
    println!("内部 x = {x}");
}

println!("外部 x = {x}"); // x 是 6

遮蔽与 mut 的区别:

  • 遮蔽可以改变变量类型,mut 不行
  • 遮蔽创建新变量,原变量被隐藏

3.2 数据类型

Rust 是静态类型语言,编译时必须知道所有变量的类型。

标量类型

整数类型:

长度 有符号 无符号
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize
  • 默认类型:i32
  • isize/usize 取决于系统架构(64位系统上为64位)

整数字面量:

rust 复制代码
let decimal = 98_222;    // 十进制(下划线可读性分隔符)
let hex = 0xff;          // 十六进制
let octal = 0o77;        // 八进制
let binary = 0b1111_0000;// 二进制
let byte = b'A';         // 字节(仅 u8)

整数溢出:

  • Debug 模式:编译时检查,溢出时 panic!
  • Release 模式:自动回绕(wrapping)
  • 安全方法:wrapping_addchecked_addoverflowing_addsaturating_add

浮点数:

rust 复制代码
let x = 2.0;      // f64(默认)
let y: f32 = 3.0; // f32

布尔类型:

rust 复制代码
let t = true;
let f: bool = false;

字符类型:

rust 复制代码
let c = 'z';
let z: char = 'ℤ';
let heart_eyed_cat = '😻';
  • 使用单引号
  • 4 字节大小
  • 表示 Unicode 标量值
复合类型

元组(Tuple):

rust 复制代码
let tup: (i32, f64, u8) = (500, 6.4, 1);

// 解构
let (x, y, z) = tup;

// 索引访问
let five_hundred = tup.0;
let six_point_four = tup.1;
  • 固定长度
  • 元素类型可以不同
  • 空元组 () 称为"单元类型"(unit)

数组(Array):

rust 复制代码
let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5]; // [3, 3, 3, 3, 3]

// 访问
let first = a[0];
let second = a[1];
  • 固定长度
  • 所有元素类型相同
  • 越界访问会在运行时 panic

3.3 函数

函数定义:

rust 复制代码
fn main() {
    println!("Hello, world!");
    another_function(5, 'h');
}

fn another_function(x: i32, unit_label: char) {
    println!("测量值: {x}{unit_label}");
}

命名规范: snake_case(小写字母 + 下划线)

语句 vs 表达式:

  • 语句 :执行操作但不返回值(如 let y = 6;
  • 表达式 :计算并返回值(如 5 + 6、函数调用、宏调用)
rust 复制代码
fn main() {
    let y = {
        let x = 3;
        x + 1  // 注意没有分号!这是表达式
    };
    println!("y = {y}"); // y = 4
}

返回值:

rust 复制代码
fn five() -> i32 {
    5  // 最后一个表达式就是返回值
}

fn plus_one(x: i32) -> i32 {
    x + 1  // 没有分号
}

// 错误示例:加了分号变成语句
fn plus_one(x: i32) -> i32 {
    x + 1;  // 编译错误!期望 i32,得到 ()
}

3.4 控制流

if 表达式
rust 复制代码
let number = 3;

if number < 5 {
    println!("条件为真");
} else {
    println!("条件为假");
}

注意: 条件必须是 bool 类型,不会自动转换!

else if:

rust 复制代码
if number % 4 == 0 {
    println!("能被 4 整除");
} else if number % 3 == 0 {
    println!("能被 3 整除");
} else {
    println!("其他");
}

if 作为表达式使用:

rust 复制代码
let condition = true;
let number = if condition { 5 } else { 6 };
// 两个分支必须返回相同类型!
循环

loop(无限循环):

rust 复制代码
loop {
    println!("永远重复!");
    break; // 退出循环
}

// 返回值
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2; // 返回值
    }
};

循环标签:

rust 复制代码
'outer: loop {
    loop {
        break 'outer; // 跳出外层循环
    }
}

while 循环:

rust 复制代码
let mut number = 3;
while number != 0 {
    println!("{number}!");
    number -= 1;
}

for 循环(最常用):

rust 复制代码
let a = [10, 20, 30, 40, 50];

for element in a {
    println!("值: {element}");
}

// 使用 Range
for number in (1..4).rev() {
    println!("{number}!");
}
println!("发射!!!");

4. 所有权系统

所有权是 Rust 最独特的特性,它让 Rust 无需垃圾回收器就能保证内存安全。

4.1 什么是所有权?

所有权规则:

  1. Rust 中的每个值都有一个所有者(owner)
  2. 同一时间只能有一个所有者
  3. 当所有者离开作用域,值将被丢弃

作用域:

rust 复制代码
{                      // s 尚未有效
    let s = "hello";   // s 从此处开始有效
    // 使用 s
}                      // 作用域结束,s 失效

4.2 String 类型与内存

rust 复制代码
let mut s = String::from("hello");
s.push_str(", world!"); // 可以修改
println!("{s}");        // hello, world!

内存分配:

  • 字符串字面量:编译时已知,硬编码到可执行文件
  • String 类型:运行时在堆上分配内存
  • Rust 在变量离开作用域时自动调用 drop 函数释放内存

4.3 移动(Move)

rust 复制代码
let s1 = String::from("hello");
let s2 = s1; // s1 被移动到 s2

// println!("{s1}"); // 编译错误!s1 已失效

浅拷贝 vs 移动:

  • 只复制栈上的数据(指针、长度、容量),不复制堆数据
  • Rust 使原变量失效,防止"双重释放"错误
  • Rust 永远不会自动创建数据的深拷贝

4.4 克隆(Clone)

rust 复制代码
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝堆数据

println!("s1 = {s1}, s2 = {s2}"); // 都有效

4.5 栈上数据的复制

rust 复制代码
let x = 5;
let y = x; // 整数实现了 Copy trait,直接复制

println!("x = {x}, y = {y}"); // 都有效

实现了 Copy 的类型:

  • 所有整数类型
  • 布尔类型
  • 浮点数类型
  • 字符类型
  • 元组(仅当所有元素都实现了 Copy)

4.6 所有权与函数

rust 复制代码
fn main() {
    let s = String::from("hello");
    takes_ownership(s); // s 被移动进函数
    // println!("{s}"); // 编译错误!s 已失效

    let x = 5;
    makes_copy(x); // x 被复制进函数
    println!("{x}"); // 仍然有效
}

fn takes_ownership(some_string: String) {
    println!("{some_string}");
} // 这里 some_string 被 drop

fn makes_copy(some_integer: i32) {
    println!("{some_integer}");
} // 这里 nothing special

返回值也转移所有权:

rust 复制代码
fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string // 返回,所有权转移给调用者
}

4.7 引用与借用

引用(Reference): 允许引用值而不获取所有权。

rust 复制代码
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("'{s1}' 的长度是 {len}"); // s1 仍然有效
}

fn calculate_length(s: &String) -> usize {
    s.len()
} // s 离开作用域,但不释放 String

借用规则:

  1. 任意时刻,你只能拥有一个可变引用任意数量的不可变引用
  2. 引用必须始终有效

可变引用:

rust 复制代码
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

限制:同一作用域只能有一个可变引用

rust 复制代码
let mut s = String::from("hello");

let r1 = &mut s;
// let r2 = &mut s; // 编译错误!不能同时有两个可变引用

不可变引用和可变引用不能共存:

rust 复制代码
let mut s = String::from("hello");

let r1 = &s;     // 没问题
let r2 = &s;     // 没问题
// let r3 = &mut s; // 编译错误!已有不可变引用

悬垂引用(Dangling Reference):

rust 复制代码
fn dangle() -> &String { // 编译错误!
    let s = String::from("hello");
    &s
} // s 被 drop,返回的引用指向无效内存

解决方案:直接返回 String

rust 复制代码
fn no_dangle() -> String {
    let s = String::from("hello");
    s // 所有权转移出去
}

4.8 切片(Slice)

切片是集合中一段连续元素的引用。

字符串切片:

rust 复制代码
let s = String::from("hello world");

let hello = &s[0..5];  // "hello"
let world = &s[6..11]; // "world"

// 省略写法
let slice = &s[0..2];  // 等价于 &s[..2]
let slice = &s[3..s.len()]; // 等价于 &s[3..]
let slice = &s[..];    // 整个字符串

字符串字面量就是切片:

rust 复制代码
let s = "Hello, world!"; // 类型是 &str

更好的函数签名:

rust 复制代码
fn first_word(s: &str) -> &str { // 接受 &String 和 &str
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

数组切片:

rust 复制代码
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // 类型是 &[i32]

5. 结构体

5.1 定义和实例化

rust 复制代码
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn main() {
    let user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("someone@example.com"),
        sign_in_count: 1,
    };

    // 修改字段(整个实例必须可变)
    let mut user2 = User { ..user1 }; // 结构体更新语法
    user2.email = String::from("another@example.com");
}

5.2 字段初始化简写

rust 复制代码
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username, // 简写:参数名与字段名相同
        email,
        sign_in_count: 1,
    }
}

5.3 结构体更新语法

rust 复制代码
let user2 = User {
    email: String::from("another@example.com"),
    ..user1 // 其余字段从 user1 获取
};
// 注意:String 字段被移动,user1 不再可用

5.4 元组结构体

rust 复制代码
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// Color 和 Point 是不同的类型!

5.5 单元结构体

rust 复制代码
struct AlwaysEqual;
let subject = AlwaysEqual;

5.6 方法

rust 复制代码
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 方法:&self 表示不可变借用
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 方法:&mut self 表示可变借用
    fn set_width(&mut self, width: u32) {
        self.width = width;
    }

    // 关联函数(不是方法):没有 &self
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    };
    println!("面积: {}", rect.area());

    let sq = Rectangle::square(3); // 关联函数调用
}

自动引用/解引用: Rust 会自动添加 &&mut* 来匹配方法签名。


6. 枚举与模式匹配

6.1 定义枚举

rust 复制代码
enum IpAddrKind {
    V4,
    V6,
}

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

6.2 枚举携带数据

rust 复制代码
enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

每个变体可以有不同的数据类型:

rust 复制代码
enum Message {
    Quit,                       // 无数据
    Move { x: i32, y: i32 },   // 具名字段
    Write(String),              // 元组结构体
    ChangeColor(i32, i32, i32), // 元组结构体
}

impl Message {
    fn call(&self) {
        // 方法体
    }
}

let m = Message::Write(String::from("hello"));
m.call();

6.3 Option 枚举

Rust 没有 null,而是使用 Option<T>

rust 复制代码
enum Option<T> {
    None,
    Some(T),
}

let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None;

Option 和普通值不能直接运算:

rust 复制代码
let x: i8 = 5;
let y: Option<i8> = Some(5);
// let sum = x + y; // 编译错误!类型不匹配

必须先将 Option<T> 转换为 T 才能操作。

6.4 match 控制流

rust 复制代码
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

绑定值的模式:

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

通配符 _

rust 复制代码
let dice_roll = 9;
match dice_roll {
    3 => add_fancy_hat(),
    7 => remove_fancy_hat(),
    _ => (), // 其他所有情况
}

6.5 if let 简洁控制流

rust 复制代码
let config_max = Some(3u8);
match config_max {
    Some(max) => println!("最大值是 {max}"),
    _ => (),
}

// 等价于
if let Some(max) = config_max {
    println!("最大值是 {max}");
}

7. 包、Crate 与模块

7.1 基本概念

  • 包(Package) :一个 Cargo.toml 描述的 Cargo 项目
  • Crate:一个模块树,可以生成库或可执行文件
  • 模块(Module):控制代码的组织、作用域和私有性

7.2 模块系统

rust 复制代码
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
        fn seat_at_table() {} // 私有
    }
}

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

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

7.3 使用 use 引入路径

rust 复制代码
use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

// 引入结构体/枚举的惯用方式
use std::collections::HashMap;

// 同名引入使用 as 别名
use std::fmt::Result;
use std::io::Result as IoResult;

7.4 模块文件拆分

src/lib.rs:

rust 复制代码
mod front_of_house; // 从 front_of_house.rs 或 front_of_house/mod.rs 加载

src/front_of_house.rs:

rust 复制代码
pub mod hosting;

src/front_of_house/hosting.rs:

rust 复制代码
pub fn add_to_waitlist() {}

8. 常用集合

8.1 Vector(动态数组)

rust 复制代码
// 创建
let v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3];

// 添加元素
let mut v = Vec::new();
v.push(5);
v.push(6);

// 读取
let third: &i32 = &v[2];          // 越界会 panic
let third: Option<&i32> = v.get(2); // 越界返回 None

// 遍历
for i in &v {
    println!("{i}");
}

// 遍历并修改
for i in &mut v {
    *i += 50;
}

8.2 字符串

rust 复制代码
// 创建
let mut s = String::new();
let s = String::from("initial content");
let s = "initial content".to_string();

// 更新
s.push_str("bar"); // 追加字符串
s.push('l');       // 追加字符
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1 被移动!注意 &s2 是 &String 自动转为 &str

// 更清晰的拼接
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}"); // 不获取所有权

// 字符串不支持索引!
// let c = s[0]; // 编译错误

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

for b in "你好".bytes() {
    println!("{b}"); // UTF-8 字节
}

8.3 HashMap

rust 复制代码
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

// 读取
let team_name = String::from("Blue");
let score = scores.get(&team_name).copied().unwrap_or(0);

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

// 插入或更新
scores.entry(String::from("Blue")).or_insert(50); // 不存在才插入

9. 错误处理

Rust 将错误分为两类:

  • 可恢复错误 :使用 Result<T, E>
  • 不可恢复错误 :使用 panic!

9.1 panic! 与不可恢复错误

rust 复制代码
fn main() {
    panic!("crash and burn");
}

使用 backtrace 调试:

bash 复制代码
RUST_BACKTRACE=1 cargo run

9.2 Result 与可恢复错误

rust 复制代码
enum Result<T, E> {
    Ok(T),
    Err(E),
}

基本用法:

rust 复制代码
use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("打开文件失败: {error:?}"),
    };
}

匹配不同错误:

rust 复制代码
use std::fs::File;
use std::io::ErrorKind;

let greeting_file = match File::open("hello.txt") {
    Ok(file) => file,
    Err(error) => match error.kind() {
        ErrorKind::NotFound => match File::create("hello.txt") {
            Ok(fc) => fc,
            Err(e) => panic!("创建文件失败: {e:?}"),
        },
        other_error => {
            panic!("打开文件失败: {other_error:?}");
        }
    },
};

快捷方法:

rust 复制代码
let greeting_file = File::open("hello.txt").unwrap();
let greeting_file = File::open("hello.txt")
    .expect("hello.txt 应该存在于项目中");

9.3 传播错误

rust 复制代码
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let username_file_result = File::open("hello.txt");

    let mut username_file = match username_file_result {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut username = String::new();

    match username_file.read_to_string(&mut username) {
        Ok(_) => Ok(username),
        Err(e) => Err(e),
    }
}

使用 ? 运算符简化:

rust 复制代码
fn read_username_from_file() -> Result<String, io::Error> {
    let mut username_file = File::open("hello.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
}

// 更简洁的链式调用
fn read_username_from_file() -> Result<String, io::Error> {
    let mut username = String::new();
    File::open("hello.txt")?.read_to_string(&mut username)?;
    Ok(username)
}

// 最简洁
fn read_username_from_file() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")
}

? 运算符的限制: 只能在返回 ResultOption 或实现了 FromResidual 的函数中使用。


10. 泛型、Trait 与生命周期

10.1 泛型

函数中的泛型:

rust 复制代码
fn largest<T>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest { // 需要 std::cmp::PartialOrd trait
            largest = item;
        }
    }
    largest
}

结构体中的泛型:

rust 复制代码
struct Point<T> {
    x: T,
    y: T,
}

let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };

多泛型参数:

rust 复制代码
struct Point<T, U> {
    x: T,
    y: U,
}

枚举中的泛型:

rust 复制代码
enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

方法中的泛型:

rust 复制代码
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

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

10.2 Trait:定义共享行为

定义 Trait:

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}

实现 Trait:

rust 复制代码
pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

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

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

默认实现:

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(阅读更多...)")
    }
}

Trait 作为参数:

rust 复制代码
pub fn notify(item: &impl Summary) {
    println!("突发新闻!{}", item.summarize());
}

// 等价于 Trait Bound 语法
pub fn notify<T: Summary>(item: &T) {
    println!("突发新闻!{}", item.summarize());
}

多个 Trait Bound:

rust 复制代码
pub fn notify(item: &(impl Summary + Display)) {}

// 泛型版本
pub fn notify<T: Summary + Display>(item: &T) {}

where 子句:

rust 复制代码
fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{
    // ...
}

返回实现 Trait 的类型:

rust 复制代码
fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from("当然,大家都知道,不是吗?"),
        reply: false,
        retweet: false,
    }
}

10.3 生命周期

生命周期确保引用始终有效。

生命周期注解语法:

rust 复制代码
&i32        // 一个引用
&'a i32     // 带有显式生命周期的引用
&'a mut i32 // 可变引用

函数中的生命周期:

rust 复制代码
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

结构体中的生命周期:

rust 复制代码
struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("找不到 '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

生命周期省略规则:

  1. 每个引用参数都有自己的生命周期参数
  2. 如果只有一个输入生命周期参数,它被赋给所有输出生命周期参数
  3. 如果有多个输入生命周期参数,但其中一个是 &self&mut self,则 self 的生命周期赋给所有输出生命周期参数

静态生命周期:

rust 复制代码
let s: &'static str = "我有一个静态生命周期";

11. 编写自动化测试

11.1 如何编写测试

Rust 中的测试是一个带有 #[test] 属性的函数。当使用 cargo test 命令时,编译器会构建并运行所有标记了 #[test] 的函数。

rust 复制代码
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = 2 + 2;
        assert_eq!(result, 4);
    }

    #[test]
    fn failing_test() {
        panic!("这个测试会失败");
    }
}

11.2 常用断言宏

rust 复制代码
#[test]
fn test_assert() {
    assert!(true);
    assert_eq!(5, 5);          // 相等断言
    assert_ne!(5, 6);          // 不等断言
}

自定义错误消息:

rust 复制代码
#[test]
fn greeting_contains_name() {
    let result = greeting("Carol");
    assert!(
        result.contains("Carol"),
        "问候语未包含姓名,结果为:`{}`", result
    );
}

11.3 使用 should_panic 测试预期 panic

rust 复制代码
#[test]
#[should_panic(expected = "小于等于 100")]
fn greater_than_100() {
    Guess::new(200);
}

11.4 使用 Result<T, E> 编写测试

rust 复制代码
#[test]
fn it_works() -> Result<(), String> {
    if 2 + 2 == 4 {
        Ok(())
    } else {
        Err(String::from("2+2 不等于 4"))
    }
}

11.5 控制测试运行

bash 复制代码
cargo test                       # 运行所有测试
cargo test -- --test-threads=1   # 单线程运行(避免竞态)
cargo test -- --show-output      # 显示 println! 输出
cargo test test_name             # 运行名称匹配的测试
cargo test -- --ignored          # 运行被忽略的测试

测试属性:

rust 复制代码
#[test]
#[ignore]  // 忽略此测试
fn expensive_test() {
    // 耗时的测试
}

11.6 测试组织

单元测试: 放在 src/ 文件中,使用 #[cfg(test)] 模块

rust 复制代码
// src/lib.rs
pub fn add_two(a: i32) -> i32 {
    internal_adder(a, 2)
}

fn internal_adder(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn internal() {
        assert_eq!(4, internal_adder(2, 2));
    }
}

集成测试: 放在 tests/ 目录下

rust 复制代码
// tests/integration_test.rs
use my_crate;

#[test]
fn it_adds_two() {
    assert_eq!(4, my_crate::add_two(2));
}

每个 tests/ 目录下的文件都是一个独立的 crate。共享代码可放在 tests/common/mod.rs 中。


12. I/O 项目实战

本章构建一个类似 grep 的命令行搜索工具。

12.1 接收命令行参数

rust 复制代码
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    let query = &args[1];
    let file_path = &args[2];
    println!("搜索:{query}");
    println!("文件:{file_path}");
}

12.2 读取文件

rust 复制代码
use std::fs;

fn main() {
    let contents = fs::read_to_string(file_path)
        .expect("读取文件失败");
    println!("文件内容:\n{contents}");
}

12.3 关注点分离

将逻辑分离到 lib.rs

rust 复制代码
// lib.rs
use std::error::Error;
use std::fs;

pub struct Config {
    pub query: String,
    pub file_path: String,
}

impl Config {
    pub fn build(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("参数不足");
        }
        let query = args[1].clone();
        let file_path = args[2].clone();
        Ok(Config { query, file_path })
    }
}

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.file_path)?;
    let results = if config.ignore_case {
        search_case_insensitive(&config.query, &contents)
    } else {
        search(&config.query, &contents)
    };
    for line in results {
        println!("{line}");
    }
    Ok(())
}

12.4 搜索函数

rust 复制代码
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

pub fn search_case_insensitive<'a>(
    query: &str,
    contents: &'a str,
) -> Vec<&'a str> {
    let query = query.to_lowercase();
    contents
        .lines()
        .filter(|line| line.to_lowercase().contains(&query))
        .collect()
}

12.5 环境变量

rust 复制代码
use std::env;

pub struct Config {
    pub query: String,
    pub file_path: String,
    pub ignore_case: bool,
}

impl Config {
    pub fn build(args: &[String]) -> Result<Config, &'static str> {
        // ...
        let ignore_case = env::var("IGNORE_CASE").is_ok();
        Ok(Config { query, file_path, ignore_case })
    }
}

12.6 将错误输出到 stderr

rust 复制代码
fn main() {
    let config = Config::build(&args).unwrap_or_else(|err| {
        eprintln!("解析参数出错:{err}");
        process::exit(1);
    });

    if let Err(e) = run(config) {
        eprintln!("应用错误:{e}");
        process::exit(1);
    }
}

13. 函数式语言特性:闭包与迭代器

13.1 闭包

闭包是可以捕获环境的匿名函数。

rust 复制代码
let add_one = |x| x + 1;
let result = add_one(5); // 6

// 带类型注解
let expensive_closure = |num: u32| -> u32 {
    println!("计算中...");
    thread::sleep(Duration::from_secs(2));
    num
};

闭包类型推断: 闭包第一次调用后,参数和返回值的类型就被固定了。

rust 复制代码
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
// let n = example_closure(5); // 编译错误!类型已固定为 String

捕获方式:

  • FnOnce:消费捕获的变量(获取所有权)
  • FnMut:可变借用
  • Fn:不可变借用
rust 复制代码
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x; // move 强制获取所有权
// println!("{:?}", x); // 编译错误!x 已被移动

13.2 使用闭包实现缓存

rust 复制代码
struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

13.3 迭代器

迭代器是惰性的: 调用消费适配器方法之前不会产生效果。

rust 复制代码
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter(); // 创建迭代器,尚未消费

for val in v1_iter {
    println!("值:{val}");
}

迭代器方法:

rust 复制代码
let v1: Vec<i32> = vec![1, 2, 3];

// 消费适配器(consuming adaptors)
let total: i32 = v1.iter().sum();

// 迭代适配器(iterator adaptors)
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();

// 链式调用
let v3: Vec<_> = v1.iter()
    .filter(|x| *x % 2 == 0)
    .map(|x| x * 2)
    .collect();

常用迭代器方法:

方法 说明
.iter() 不可变引用迭代器
.iter_mut() 可变引用迭代器
.into_iter() 获取所有权的迭代器
.map() 转换每个元素
.filter() 过滤元素
.fold() 折叠/归约
.collect() 收集到集合
.sum() 求和
.count() 计数
.zip() 拉链合并
.enumerate() 带索引枚举
.skip() 跳过前 n 个
.take() 取前 n 个
.chain() 链式连接两个迭代器

13.4 自定义迭代器

rust 复制代码
struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

let sum: u32 = Counter::new()
    .zip(Counter::new().skip(1))
    .map(|(a, b)| a * b)
    .filter(|x| x % 3 == 0)
    .sum();

13.5 迭代器性能

迭代器是 Rust 中的零成本抽象(zero-cost abstraction)。使用迭代器的代码通常和手写底层循环一样快,甚至更快。


14. Cargo 与 Crates.io 进阶

14.1 自定义构建配置

toml 复制代码
[profile.dev]
opt-level = 0  # 开发模式,不优化

[profile.release]
opt-level = 3  # 发布模式,最大优化

14.2 发布 Crate 到 Crates.io

文档注释:

rust 复制代码
/// 将传入的数字加 1
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

生成文档:

bash 复制代码
cargo doc --open

常用文档章节:

  • # Examples --- 示例
  • # Panics --- 何时可能 panic
  • # Errors --- 返回 Error 的情况
  • # Safety --- unsafe 函数的安全要求

发布流程:

bash 复制代码
cargo login          # 登录
cargo publish        # 发布
cargo yank --vers 1.0.1  # 撤回版本
cargo yank --vers 1.0.1 --undo  # 取消撤回

14.3 工作空间(Workspace)

大型项目可以组织为工作空间:

toml 复制代码
# Cargo.toml(根目录)
[workspace]
members = [
    "adder",
    "add-one",
]
bash 复制代码
cargo build    # 构建整个工作空间
cargo test -p add-one  # 测试特定包

14.4 使用 cargo install 安装二进制 crate

bash 复制代码
cargo install ripgrep

15. 智能指针

智能指针是实现了 DerefDrop trait 的数据结构。

15.1 Box:堆分配

Box<T> 将数据分配在堆上,而不是栈上。

rust 复制代码
let b = Box::new(5);
println!("b = {b}");

递归类型: 使用 Box 打破编译时大小未知的问题

rust 复制代码
enum List {
    Cons(i32, Box<List>),
    Nil,
}

let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

15.2 Deref Trait

实现 Deref 后,* 解引用运算符可以自定义行为:

rust 复制代码
use std::ops::Deref;

struct MyBox<T>(T);

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

let x = 5;
let y = MyBox(x);
assert_eq!(5, *y); // 实际上调用 *(y.deref())

解引用强制多态(Deref Coercion): Rust 自动将实现了 Deref 的类型的引用转换为对目标类型的引用。

rust 复制代码
fn hello(name: &str) {
    println!("Hello, {name}!");
}

let m = MyBox(String::from("Rust"));
hello(&m); // &MyBox<String> → &String → &str

15.3 Drop Trait

当值离开作用域时自动调用 drop

rust 复制代码
struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("释放 CustomSmartPointer 数据:`{}`", self.data);
    }
}

let c = CustomSmartPointer { data: String::from("my stuff") };
let d = CustomSmartPointer { data: String::from("other stuff") };
println!("CustomSmartPointers created");
// 离开作用域时自动释放,顺序与创建相反:d 先,c 后

提前释放: 使用 std::mem::drop 函数

rust 复制代码
drop(c); // 提前释放

15.4 Rc:引用计数智能指针

Rc<T> 允许一个值有多个所有者(只读,单线程)。

rust 复制代码
use std::rc::Rc;

enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use crate::List::{Cons, Nil};

let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a)); // 引用计数 +1
let c = Cons(4, Rc::clone(&a)); // 引用计数 +1

获取引用计数:

rust 复制代码
println!("计数 = {}", Rc::strong_count(&a));

15.5 RefCell 与内部可变性

RefCell<T> 在运行时检查借用规则(单线程)。

rust 复制代码
use std::cell::RefCell;

let data = RefCell::new(5);

// 可变借用
*data.borrow_mut() += 1;

// 不可变借用
let value = data.borrow();
println!("{}", *value);

运行时借用检查:

  • borrow() 返回 Ref<T>(不可变引用)
  • borrow_mut() 返回 RefMut<T>(可变引用)
  • 违反规则时在运行时 panic,而不是编译错误

选择指南:

类型 所有者 借用检查 适用场景
Box<T> 单一 编译时 堆分配
Rc<T> 多个 编译时 共享只读数据
RefCell<T> 单一 运行时 内部可变性
Rc<RefCell<T>> 多个 运行时 共享可变数据

15.6 循环引用与 Weak

使用 Weak<T> 避免循环引用:

rust 复制代码
use std::rc::{Rc, Weak};

struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}

let leaf = Rc::new(Node {
    value: 3,
    parent: RefCell::new(Weak::new()),
    children: RefCell::new(vec![]),
});

let branch = Rc::new(Node {
    value: 5,
    parent: RefCell::new(Weak::new()),
    children: RefCell::new(vec![Rc::clone(&leaf)]),
});

*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
  • Rc::downgrade 创建 Weak<T>
  • Weak::upgrade 返回 Option<Rc<T>>
  • Weak<T> 不增加 strong_count,不会阻止值被释放

16. 无畏并发

16.1 使用线程

rust 复制代码
use std::thread;
use std::time::Duration;

let handle = thread::spawn(|| {
    for i in 1..10 {
        println!("线程中的数字 {i}");
        thread::sleep(Duration::from_millis(1));
    }
});

for i in 1..5 {
    println!("主线程中的数字 {i}");
    thread::sleep(Duration::from_millis(1));
}

handle.join().unwrap(); // 等待线程完成

使用 move 闭包:

rust 复制代码
let v = vec![1, 2, 3];

let handle = thread::spawn(move || {
    println!("向量:{v:?}");
});

handle.join().unwrap();

16.2 消息传递

使用通道(channel)在线程间传递消息:

rust 复制代码
use std::sync::mpsc;
use std::thread;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    let val = String::from("你好");
    tx.send(val).unwrap();
    // println!("{val}"); // 编译错误!所有权已转移
});

let received = rx.recv().unwrap();
println!("收到:{received}");

多个发送者:

rust 复制代码
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();

thread::spawn(move || {
    tx1.send("来自线程 1").unwrap();
});

thread::spawn(move || {
    tx.send("来自线程 2").unwrap();
});

for received in rx {
    println!("收到:{received}");
}

16.3 共享状态

使用 Mutex<T>Arc<T>

rust 复制代码
use std::sync::{Arc, Mutex};
use std::thread;

let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}

println!("结果:{}", *counter.lock().unwrap()); // 10

关键点:

  • Mutex<T> 提供互斥访问(内部可变性)
  • Arc<T> 是线程安全的引用计数(原子操作)
  • MutexGuard 离开作用域时自动释放锁
  • 注意死锁风险

16.4 Send 和 Sync Trait

  • Send: 类型的所有权可以在线程间转移。几乎所有 Rust 类型都是 Send,但 Rc<T> 不是
  • Sync: 类型可以被多个线程同时引用。Rc<T> 不是 Sync,Arc<T>

17. Async 与 Await

17.1 异步基础

Rust 使用 async/await 语法进行异步编程:

rust 复制代码
use std::time::Duration;

async fn do_something() {
    // 异步操作
}

#[tokio::main]
async fn main() {
    let future = do_something(); // 创建 Future,尚未执行
    future.await; // 等待 Future 完成
}

17.2 Future Trait

rust 复制代码
trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}

17.3 async 块

rust 复制代码
let future = async {
    println!("异步块");
    42
};
let result = future.await;

17.4 并发执行

rust 复制代码
use tokio::time::{sleep, Duration};

async fn task1() {
    sleep(Duration::from_secs(1)).await;
    println!("任务 1 完成");
}

async fn task2() {
    sleep(Duration::from_secs(2)).await;
    println!("任务 2 完成");
}

#[tokio::main]
async fn main() {
    // 并发执行
    tokio::join!(task1(), task2());
}

17.5 Pin 与 Unpin

  • Pin:确保值不被移动
  • Unpin:标记类型可以安全移动(大多数类型都是 Unpin)
  • !Unpin:标记类型不能移动(如自引用结构体)

18. 面向对象编程特性

18.1 Rust 中的 OOP 特性

封装: Rust 通过 pub 关键字控制可见性

rust 复制代码
pub struct AveragedCollection {
    list: Vec<i32>,
    average: f64,
}

impl AveragedCollection {
    pub fn add(&mut self, value: i32) {
        self.list.push(value);
        self.update_average();
    }

    pub fn average(&self) -> f64 {
        self.average
    }

    fn update_average(&mut self) {
        let total: i32 = self.list.iter().sum();
        self.average = total as f64 / self.list.len() as f64;
    }
}

继承: Rust 没有传统意义上的继承,而是使用:

  • 默认 trait 方法 实现代码复用
  • 泛型与 trait bound 实现多态

18.2 使用 Trait 对象实现多态

rust 复制代码
pub trait Draw {
    fn draw(&self);
}

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>,
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

pub struct Button {
    pub width: u32,
    pub height: u32,
    pub label: String,
}

impl Draw for Button {
    fn draw(&self) {
        // 绘制按钮
    }
}

// 用户自定义类型
struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        // 绘制选择框
    }
}

fn main() {
    let screen = Screen {
        components: vec![
            Box::new(SelectBox { /* ... */ }),
            Box::new(Button { /* ... */ }),
        ],
    };
    screen.run();
}

动态分发(Dynamic Dispatch): 使用 dyn Trait 时,Rust 在运行时通过虚函数表(vtable)确定调用哪个方法,带来少量性能开销。

18.3 状态模式

Rust 可以使用 trait 对象实现状态模式:

rust 复制代码
pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;
    fn content<'a>(&self, post: &'a Post) -> &'a str {
        ""
    }
}

struct Draft {}
impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }
    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}
impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }
    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}
impl State for Published {
    fn request_review(self: Box<Self>) -> Box<dyn State> { self }
    fn approve(self: Box<Self>) -> Box<dyn State> { self }
    fn content<'a>(&self, post: &'a Post) -> &'a str {
        &post.content
    }
}

19. 模式与匹配

19.1 所有模式语法

rust 复制代码
// 字面量
match x {
    1 => println!("一"),
    2 => println!("二"),
    _ => println!("其他"),
}

// 命名变量
match x {
    Some(y) => println!("{y}"),
    None => (),
}

// 多模式
match x {
    1 | 2 => println!("一或二"),
    _ => println!("其他"),
}

// 范围
match x {
    1..=5 => println!("一到五"),
    _ => println!("其他"),
}

// 解构
let (a, b, c) = (1, 2, 3);

// 解构结构体
let Point { x, y } = point;

// 解构枚举
match msg {
    Message::Quit => (),
    Message::Write(text) => println!("{text}"),
    Message::Move { x, y } => println!("移动到 ({x}, {y})"),
}

// 守卫
match x {
    n if n < 5 => println!("小于五"),
    n => println!("{n}"),
}

// @ 绑定
match x {
    n @ 1..=5 => println!("匹配到 {n}"),
    _ => (),
}

19.2 忽略值

rust 复制代码
// 使用 _ 忽略整个值
fn foo(_: i32) {}

// 使用 _ 忽略部分值
match origin {
    (0, _, _) => println!("在 x 轴上"),
    (_, 0, _) => println!("在 y 轴上"),
    _ => println!("其他位置"),
}

// 使用 .. 忽略剩余部分
match origin {
    (0, ..) => println!("在 x 轴上"),
    (.., 0) => println!("在 z 轴上"),
    _ => println!("其他位置"),
}

19.3 ref 和 ref mut

rust 复制代码
let mut robot_name = Some(String::from("Bors"));

match robot_name {
    Some(ref name) => println!("名称:{name}"), // 借用而非移动
    None => (),
}

match robot_name {
    Some(ref mut name) => *name = String::from("另一个名称"),
    None => (),
}

20. 高级特性

20.1 Unsafe Rust

Unsafe Rust 提供 5 种超级能力:

rust 复制代码
unsafe {
    // 1. 解引用裸指针
    let mut num = 5;
    let r1 = &raw const num;
    let r2 = &raw mut num;
    println!("r1: {}", *r1);
    println!("r2: {}", *r2);

    // 2. 调用 unsafe 函数
    dangerous();

    // 3. 访问或修改可变静态变量
    static mut COUNTER: u32 = 0;
    COUNTER += 1;
}

// 4. 实现 unsafe trait
unsafe trait Foo {}
unsafe impl Foo for i32 {}

// 5. 访问联合体字段

裸指针:

rust 复制代码
let mut num = 5;
let r1 = &raw const num;   // *const i32
let r2 = &raw mut num;     // *mut i32

FFI(外部函数接口):

rust 复制代码
unsafe extern "C" {
    safe fn abs(input: i32) -> i32;
    fn sqrt(input: f64) -> f64;
}

fn main() {
    println!("绝对值为:{}", abs(-3));
}

从 Rust 导出函数给 C 调用:

rust 复制代码
#[no_mangle]
pub extern "C" fn call_from_c() {
    println!("从 C 调用 Rust 函数!");
}

20.2 高级 Trait

关联类型:

rust 复制代码
pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        // ...
    }
}

默认泛型参数与运算符重载:

rust 复制代码
use std::ops::Add;

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Point;
    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

// 自定义 Rhs 类型
struct Millimeters(u32);
struct Meters(u32);

impl Add<Meters> for Millimeters {
    type Output = Millimeters;
    fn add(self, other: Meters) -> Millimeters {
        Millimeters(self.0 + (other.0 * 1000))
    }
}

完全限定语法:

rust 复制代码
<Type as Trait>::function(receiver_if_method, next_arg, ...);

// 示例
<Dog as Animal>::baby_name();

Supertrait:

rust 复制代码
trait OutlinePrint: fmt::Display {
    fn outline_print(&self) {
        let output = self.to_string();
        let len = output.len();
        println!("{}", "*".repeat(len + 4));
        println!("* {} *", " ".repeat(len + 2));
        println!("* {output} *");
        println!("* {} *", " ".repeat(len + 2));
        println!("{}", "*".repeat(len + 4));
    }
}

Newtype 模式: 在外部类型上实现外部 trait

rust 复制代码
struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

20.3 高级类型

类型别名:

rust 复制代码
type Kilometers = i32;
type Thunk = Box<dyn Fn() + Send + 'static>;
type Result<T> = std::result::Result<T, std::io::Error>;

Never 类型 !

rust 复制代码
fn bar() -> ! {
    panic!();
}

// continue 和 panic! 的类型都是 !
let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue, // ! 类型,可转换为任何类型
};

动态大小类型(DST):

rust 复制代码
// str 是 DST,必须放在指针后面
let s: &str = "hello";

// Sized trait 默认约束
fn generic<T: Sized>(t: T) {}

// ?Sized 放宽约束
fn generic<T: ?Sized>(t: &T) {}

20.4 高级函数与闭包

函数指针:

rust 复制代码
fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

let answer = do_twice(add_one, 5); // 12

返回闭包:

rust 复制代码
fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

// 返回不同闭包时使用 trait 对象
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

20.5 宏

声明宏(macro_rules!):

rust 复制代码
#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

过程宏: 三种类型

  1. 自定义 derive 宏:
rust 复制代码
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro();
}
  1. 属性宏:
rust 复制代码
#[route(GET, "/")]
fn index() {}
  1. 函数宏:
rust 复制代码
let sql = sql!(SELECT * FROM users WHERE id = 1);

21. 最终项目:构建多线程 Web 服务器

21.1 单线程 Web 服务器

rust 复制代码
use std::{
    fs,
    io::{prelude::*, BufReader},
    net::{TcpListener, TcpStream},
};

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();

    for stream in listener.incoming() {
        let stream = stream.unwrap();
        handle_connection(stream);
    }
}

fn handle_connection(mut stream: TcpStream) {
    let buf_reader = BufReader::new(&mut stream);
    let request_line = buf_reader.lines().next().unwrap().unwrap();

    let (status_line, filename) = if request_line == "GET / HTTP/1.1" {
        ("HTTP/1.1 200 OK", "hello.html")
    } else {
        ("HTTP/1.1 404 NOT FOUND", "404.html")
    };

    let contents = fs::read_to_string(filename).unwrap();
    let length = contents.len();

    let response = format!(
        "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"
    );

    stream.write_all(response.as_bytes()).unwrap();
}

21.2 线程池实现

rust 复制代码
use std::{
    sync::{mpsc, Arc, Mutex},
    thread,
};

pub struct ThreadPool {
    workers: Vec<Worker>,
    sender: Option<mpsc::Sender<Job>>,
}

type Job = Box<dyn FnOnce() + Send + 'static>;

impl ThreadPool {
    /// 创建线程池
    ///
    /// # Panics
    ///
    /// size 为 0 时 panic
    pub fn new(size: usize) -> ThreadPool {
        assert!(size > 0);

        let (sender, receiver) = mpsc::channel();
        let receiver = Arc::new(Mutex::new(receiver));

        let mut workers = Vec::with_capacity(size);

        for id in 0..size {
            workers.push(Worker::new(id, Arc::clone(&receiver)));
        }

        ThreadPool {
            workers,
            sender: Some(sender),
        }
    }

    pub fn execute<F>(&self, f: F)
    where
        F: FnOnce() + Send + 'static,
    {
        let job = Box::new(f);
        self.sender.as_ref().unwrap().send(job).unwrap();
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        drop(self.sender.take()); // 关闭通道

        for worker in &mut self.workers {
            println!("关闭工作线程 {}", worker.id);

            if let Some(thread) = worker.thread.take() {
                thread.join().unwrap();
            }
        }
    }
}

struct Worker {
    id: usize,
    thread: Option<thread::JoinHandle<()>>,
}

impl Worker {
    fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
        let thread = thread::spawn(move || loop {
            let message = receiver.lock().unwrap().recv();

            match message {
                Ok(job) => {
                    println!("工作线程 {id} 获取到任务,正在执行");
                    job();
                }
                Err(_) => {
                    println!("工作线程 {id} 断开连接,正在关闭");
                    break;
                }
            }
        });

        Worker {
            id,
            thread: Some(thread),
        }
    }
}

21.3 使用线程池

rust 复制代码
fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    let pool = ThreadPool::new(4);

    for stream in listener.incoming().take(2) {
        let stream = stream.unwrap();

        pool.execute(|| {
            handle_connection(stream);
        });
    }

    println!("服务器关闭");
}

附录:常用资源

相关推荐
咸甜适中2 小时前
rust语言学习笔记Trait(十五)Drop(释放资源)
笔记·学习·rust
IT笔记2 小时前
【Rust】 Rust宏学习笔记
笔记·学习·rust
Niyy_4 小时前
Rust 学习笔记 01
笔记·学习·rust
fox_lht5 小时前
14.3.重构
开发语言·后端·rust
小宇子2B5 小时前
所有权和生命周期不是新东西:编译器接管内存管理的五十年
rust
fox_lht6 小时前
13.3.测试的组织方式
开发语言·后端·rust
红藕香残玉簟秋7 小时前
【Rust学习】windows安装rust
开发语言·学习·rust
fox_lht8 小时前
第十四章 一个输入和输出项目:构建一个命令行程序
开发语言·后端·rust
fox_lht8 小时前
14.2.读文件
开发语言·后端·rust