目录
- 引言与安装
- 猜数字游戏实战
- 常见编程概念
- 所有权系统
- 结构体
- 枚举与模式匹配
- [包、Crate 与模块](#包、Crate 与模块)
- 常用集合
- 错误处理
- [泛型、Trait 与生命周期](#泛型、Trait 与生命周期)
- 编写自动化测试
- [I/O 项目实战](#I/O 项目实战)
- 函数式语言特性:闭包与迭代器
- [Cargo 与 Crates.io 进阶](#Cargo 与 Crates.io 进阶)
- 智能指针
- 无畏并发
- [Async 与 Await](#Async 与 Await)
- 面向对象编程特性
- 模式与匹配
- 高级特性
- [最终项目:构建多线程 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();--- 创建可变变量,绑定到一个空的StringString::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;--- 导入随机数生成 traitrand::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_add、checked_add、overflowing_add、saturating_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 什么是所有权?
所有权规则:
- Rust 中的每个值都有一个所有者(owner)
- 同一时间只能有一个所有者
- 当所有者离开作用域,值将被丢弃
作用域:
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
借用规则:
- 任意时刻,你只能拥有一个可变引用 或任意数量的不可变引用
- 引用必须始终有效
可变引用:
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")
}
? 运算符的限制: 只能在返回 Result、Option 或实现了 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,
};
}
生命周期省略规则:
- 每个引用参数都有自己的生命周期参数
- 如果只有一个输入生命周期参数,它被赋给所有输出生命周期参数
- 如果有多个输入生命周期参数,但其中一个是
&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. 智能指针
智能指针是实现了 Deref 和 Drop 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
}
};
}
过程宏: 三种类型
- 自定义 derive 宏:
rust
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
- 属性宏:
rust
#[route(GET, "/")]
fn index() {}
- 函数宏:
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!("服务器关闭");
}
附录:常用资源
- 官方文档: https://doc.rust-lang.org/
- Cargo 包注册中心: https://crates.io/
- Rust Playground: https://play.rust-lang.org/
- Rust 标准库文档: https://doc.rust-lang.org/std/
- Rust 参考手册: https://doc.rust-lang.org/reference/
- Rust 版次指南: https://doc.rust-lang.org/edition-guide/
- Rust 异步编程: https://rust-lang.github.io/async-book/