Rust 教程
什么是 Rust?
Rust 是由 Mozilla 主导开发(现由 Rust 基金会维护)的系统级编程语言,2015 年发布 1.0。它以内存安全与高性能著称,连续多年被评为"最受喜爱的编程语言"。
Rust 的核心特点
- 内存安全:所有权(Ownership)系统在编译期杜绝空指针、悬垂引用、数据竞争
- 零成本抽象:高级语言体验,接近 C/C++ 的性能
- 无垃圾回收:内存由所有权机制自动管理
- 并发安全:Send/Sync 特性保证线程安全
- 工具链完善:Cargo 包管理、rustfmt 格式化、clippy 检查
Rust 能做什么
- 系统软件:操作系统、驱动、嵌入式
- 高性能服务:Web 后端(Axum/Actix)、网络代理
- 命令行工具(ripgrep、fd、bat 都是 Rust 写的)
- 区块链与智能合约
- WebAssembly 前端
Hello World
fn main() {
println!("Hello, world!");
}
# 编译运行
rustc hello.rs
./hello
# 或使用 Cargo
cargo new hello
cargo run
Rust 简介
Rust 简介
Rust 的设计目标是在不牺牲性能的前提下提供内存安全,其核心创新是所有权系统。
Rust 的诞生
2006 Graydon Hoare 个人项目
2009 Mozilla 资助
2015 1.0 发布
2016+ 连续多年最受欢迎语言
2021 Rust 基金会成立
2023+ Windows/Linux 内核开始接纳 Rust
核心设计理念
1. 安全优先:
编译期捕获大多数内存错误
C 语言常见 bug(use-after-free、缓冲区溢出)在 Rust 中无法编译通过
2. 性能优先:
无 GC、无运行时开销
零成本抽象: 高级语法编译成高效机器码
3. 并发安全:
Send + Sync 特性由编译器验证
数据竞争在编译期就被拒绝
Rust vs 其他语言
vs C/C++:
Rust 提供了 C 的性能,但内存安全由编译器保证
Cargo 让依赖管理像 npm/pip 一样简单
vs Go:
Go 简单易学、有 GC;Rust 更精细控制、性能更高
两者在服务端都常用
vs Python:
Python 开发快;Rust 性能强
Rust 常用来给 Python 写高性能扩展
生态概览
包管理: Cargo + crates.io(100 万+ crate)
Web: Axum、Actix-web、Rocket
异步: Tokio、async-std
数据库: sqlx、Diesel、sea-orm
序列化: serde
测试: 内置 + proptest
LSP: rust-analyzer
适合人群
- 系统/嵌入式开发者
- 追求性能的 Web 后端
- CLI 工具开发者
- 对内存安全要求高的团队
- 想理解现代编译期安全机制的学习者
Rust 环境搭建
Rust 环境搭建
安装 Rust 推荐使用 rustup 工具链管理器,一条命令搞定。
1. 安装 rustup
# macOS / Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows
下载 rustup-init.exe 安装
# 安装完成后配置 PATH(一般自动)
source "$HOME/.cargo/env"
2. 验证安装
rustc --version # 编译器版本
cargo --version # 包管理版本
rustup --version # 工具链管理器
3. 更新与卸载
rustup update # 更新工具链
rustup self uninstall # 卸载
rustup show # 查看已安装组件
4. 安装 IDE 插件
VS Code:
1. rust-analyzer 插件(语言支持)
2. Even Better TOML(配置高亮)
3. crates(依赖版本提示)
IntelliJ:
Rust 插件
# 安装 rust-analyzer 命令行
rustup component add rust-analyzer
5. 第一个 Cargo 项目
# 新建项目
cargo new hello_world
cd hello_world
# 目录结构
# hello_world/
# ├── Cargo.toml # 项目配置
# └── src/
# └── main.rs # 源码
# 编译并运行
cargo run
# 仅编译
cargo build
# 发布模式(优化)
cargo build --release
6. 常见问题
1. 编译慢: 首次编译依赖多,正常;release 更慢
2. 依赖下载慢: 配置国内镜像(rsproxy.cn)
3. Windows 链接错误: 安装 Visual Studio Build Tools
4. rustc 找不到: source ~/.cargo/env
Cargo 教程
Cargo 教程
Cargo 是 Rust 的构建系统与包管理器,相当于 npm + webpack。
1. 项目配置(Cargo.toml)
[package]
name = "my_app" # 包名
version = "0.1.0" # 版本
edition = "2021" # Rust 版本
authors = ["You"]
[dependencies] # 依赖
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
[dev-dependencies] # 测试依赖
criterion = "0.5"
[profile.release] # 发布优化
opt-level = 3
lto = true
2. 常用命令
cargo new # 新建项目(二进制)
cargo new --lib # 新建库项目
cargo build # 编译(debug)
cargo build --release # 编译(优化)
cargo run # 编译并运行
cargo run --release # 发布模式运行
cargo check # 快速检查(不生成二进制)
cargo test # 运行测试
cargo fmt # 格式化代码
cargo clippy # 静态检查
cargo doc # 生成文档
cargo add serde # 添加依赖(自动改 Cargo.toml)
cargo remove serde # 移除依赖
cargo tree # 查看依赖树
cargo update # 更新依赖版本
3. 添加依赖
# 方式一: 命令行
cargo add serde --features derive
cargo add tokio --features full
cargo add rand --dev # 开发依赖
# 方式二: 手动编辑 Cargo.toml 后 cargo build
4. 国内镜像配置
# ~/.cargo/config.toml
[source.crates-io]
replace-with = "rsproxy-sparse"
[source.rsproxy-sparse]
registry = "sparse+https://rsproxy.cn/index/"
[registries.rsproxy]
index = "sparse+https://rsproxy.cn/index/"
[net]
git-fetch-with-cli = true
5. workspace(多包项目)
# 根 Cargo.toml
[workspace]
members = ["core", "server", "cli"]
resolver = "2"
# 各子目录有自己的 Cargo.toml
# 在根目录统一构建:
cargo build --workspace
6. 发布 crate
cargo login # 登录 crates.io
cargo publish # 发布
cargo publish --dry-run # 预演
Rust 输出到命令行
Rust 输出到命令行
掌握 println! 等宏,是学习 Rust 的第一步。
1. println! 基础
fn main() {
println!("Hello, world!"); // 带换行
print!("不换行"); // 不带换行
// 格式化占位符
println!("数字: {}", 42);
println!("{} + {} = {}", 1, 2, 3);
// 位置参数
println!("{1} 和 {0}", "先", "后"); // 后 和 先
// 命名参数
println!("{name} 今年 {age} 岁", name = "张三", age = 28);
}
2. 格式化语法
// 宽度与对齐
println!("|{:5}|", 42); // | 42| 右对齐
println!("|{:<5}|", 42); // |42 | 左对齐
println!("|{:^5}|", 42); // | 42 | 居中
// 数字格式
println!("{:b}", 10); // 1010 二进制
println!("{:o}", 10); // 12 八进制
println!("{:x}", 255); // ff 十六进制
println!("{:X}", 255); // FF 大写
println!("{:.2}", 3.14159); // 3.14 小数位
println!("{:e}", 1000.0); // 1e3 科学计数
// 补零
println!("{:04}", 42); // 0042
// Debug 格式
let v = vec![1, 2, 3];
println!("{:?}", v); // [1, 2, 3] Debug
println!("{:#?}", v); // 美化多行 Debug
3. Display vs Debug
// Display: {} 人类可读
// Debug: {:?} 调试输出
#[derive(Debug)]
struct Point { x: i32, y: i32 }
fn main() {
let p = Point { x: 1, y: 2 };
println!("{:?}", p); // Point { x: 1, y: 2 }
// 需要手动实现 Display 才能用 {}
// impl std::fmt::Display for Point { ... }
}
4. eprintln! 错误输出
fn main() {
// stdout: 正常输出
println!("正常输出");
// stderr: 错误输出(不混入管道数据)
eprintln!("错误信息");
}
// 管道测试
// cargo run 2>err.log # 错误进文件
5. 读取输入
use std::io;
fn main() {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("读取失败");
println!("你输入了: {}", input.trim());
}
Rust 基础语法
Rust 基础语法
Rust 语法融合了 C 的简洁与现代语言的安全特性。
1. 变量与不可变性
fn main() {
// 默认不可变
let x = 5;
// x = 6; // 错误!x 不可变
// mut 声明可变
let mut y = 5;
y = 6; // 可以
println!("y = {}", y);
}
2. 常量与类型标注
const MAX_POINTS: u32 = 100_000; // 常量(编译期)
fn main() {
let x: i32 = 5; // 显式类型
let f: f64 = 3.14; // 浮点
let b: bool = true; // 布尔
let c: char = 'A'; // 字符
let s: &str = "Hello"; // 字符串切片
}
3. 类型推断
let x = 5; // i32
let f = 3.14; // f64
let s = "hello"; // &str
let v = vec![1, 2, 3]; // Vec
let t = (1, "a"); // (i32, &str)
4. 遮蔽(Shadowing)
let x = 5;
let x = x + 1; // 遮蔽,新变量 x = 6
let x = x * 2; // x = 12
println!("{}", x); // 12
// 遮蔽可改变类型
let s = "123";
let s = s.len(); // &str → usize
5. 基本数据类型
// 整数
i8 i16 i32 i64 i128 有符号
u8 u16 u32 u64 u128 无符号
usize isize 与指针同宽
// 浮点
f32 f64
// 布尔 / 字符
bool char(4 字节 Unicode)
// 复合
tuple: (i32, f64, &str)
array: [i32; 5] 固定长度
6. 表达式与语句
// 表达式: 有值(无分号)
let y = {
let x = 3;
x + 1 // 表达式,无分号
}; // y = 4
// 语句: 无值(有分号)
let z = 5; // 语句
// if 也是表达式
let result = if x > 5 { "大" } else { "小" };
7. 注释
// 行注释
/* 块注释 */
/// 文档注释(生成文档)
/// # 示例
/// ```
/// let x = 5;
/// ```
fn example() {}
//! 模块级文档注释
Rust 运算符
Rust 运算符
Rust 提供完整的运算符支持,部分可重载。
1. 算术运算符
let a = 10;
let b = 3;
a + b // 13
a - b // 7
a * b // 30
a / b // 3(整数除法)
a % b // 1 取余
// 浮点除法
10.0 / 3.0 // 3.333...
// 注意整数除法会截断
// 溢出行为(debug 模式 panic,release 回绕)
// 需要显式处理用 checked_add 等
2. 复合赋值
let mut x = 10;
x += 5; // 15
x -= 3; // 12
x *= 2; // 24
x /= 4; // 6
x %= 4; // 2
3. 比较运算符
== != < > <= >=
let a = 5;
a == 5 // true
a != 3 // true
a < 10 // true
a >= 5 // true
4. 逻辑运算符
&& 与(短路)
|| 或(短路)
! 非
let age = 25;
age > 18 && age < 60 // true
age < 18 || age > 60 // false
!(age > 18) // false
5. 位运算符
let a: u8 = 0b1100;
let b: u8 = 0b1010;
a & b // 0b1000 与
a | b // 0b1110 或
a ^ b // 0b0110 异或
!a // 取反
a << 1 // 左移 1 位
a >> 1 // 右移 1 位
6. 类型转换
// as 转换
let x: i32 = 5;
let y: f64 = x as f64; // 5.0
let z: u8 = 255 as u8; // 255
let c: char = 65 as char; // 'A'
let n: i32 = 'A' as i32; // 65
// 需要注意: 大转小会截断
let big: i32 = 300;
let small: u8 = big as u8; // 44(截断)
// 字符串解析
let n: i32 = "42".parse().unwrap(); // 42
let f: f64 = "3.14".parse().unwrap(); // 3.14
// 或
let n = "42".parse::().unwrap();
7. 运算符重载
use std::ops::Add;
#[derive(Debug, 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 }
}
}
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };
println!("{:?}", p1 + p2); // Point { x: 4, y: 6 }
Rust 数据类型
Rust 数据类型
Rust 是静态强类型语言,类型系统是其安全性的基石。
1. 标量类型
// 整数
let a: i32 = -100; // 有符号
let b: u64 = 100; // 无符号
let c: isize = 0; // 与平台相关
// 浮点
let pi: f32 = 3.14;
let e: f64 = 2.71828;
// 布尔
let t: bool = true;
let f: bool = false;
// 字符(4 字节,Unicode)
let ch: char = '中';
let emoji: char = '😀';
2. 元组(Tuple)
let tup: (i32, f64, char) = (500, 6.4, 'a');
// 解构
let (x, y, z) = tup;
println!("{} {} {}", x, y, z); // 500 6.4 a
// 索引访问
println!("{}", tup.0); // 500
println!("{}", tup.2); // a
// 单元素元组需要逗号
let single = (5,); // 注意逗号
3. 数组(Array)
// 固定长度
let arr: [i32; 3] = [1, 2, 3];
// 相同值
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
// 访问
println!("{}", arr[0]); // 1
println!("长度: {}", arr.len());
// 越界会 panic(安全!)
// println!("{}", arr[5]); // 编译错误/运行时 panic
// 遍历
for x in arr {
println!("{}", x);
}
4. 切片(Slice)
let arr = [1, 2, 3, 4, 5];
let slice: &[i32] = &arr[1..3]; // [2, 3]
let all: &[i32] = &arr[..]; // 全部
let tail: &[i32] = &arr[2..]; // [3, 4, 5]
// 字符串切片
let s = String::from("hello");
let part = &s[0..2]; // "he"(按字节)
5. 字符串
// &str: 字符串切片(不可变借用)
let s1: &str = "hello";
// String: 可增长字符串
let mut s2 = String::from("hello");
s2.push_str(" world");
// 转换
let s3 = s1.to_string();
let s4 = &s2[..]; // String → &str
// 注意: 不能直接索引 s[0](UTF-8)
// 用 chars() 遍历
for c in s2.chars() {
println!("{}", c);
}
6. 类型别名与转换
type Age = u8; // 类型别名
let age: Age = 30;
// 常用转换
let s = 42.to_string(); // "42"
let n: i32 = "42".parse().unwrap();
let f: f64 = 3.14;
let i: i32 = f as i32; // 3(截断)
Rust 注释
Rust 注释
Rust 的注释体系完善,支持行注释、文档注释与代码示例测试。
1. 基本注释
// 行注释
// Rust 中 // 是标准注释方式
/* 块注释 */
/* 可以
多行 */
fn main() {
// 代码中的注释
let x = 5; // 行尾注释
}
2. 文档注释(///)
/// 计算两数之和
///
/// # 参数
/// - `a`: 第一个数
/// - `b`: 第二个数
///
/// # 返回
/// a + b 的结果
///
/// # 示例
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
3. 模块文档(//!)
//! 这是模块级文档注释
//! 描述整个模块的用途与用法
//! 放在文件或模块开头
/// 函数级文档
pub fn foo() {}
// 生成文档: cargo doc --open
4. 文档测试
/// ```
/// let x = 5;
/// assert!(x > 0);
/// ```
pub fn documented() {}
// cargo test 会运行文档中的代码块!
// 文档中的示例可以当测试用
// 忽略测试的示例
/// ```ignore
/// 不会执行的代码
/// ```
/// ```no_run
/// 编译但不运行
/// ```
5. 常见注释标记
// TODO: 待完成的功能
// FIXME: 需要修复的问题
// HACK: 临时方案,需要改进
// NOTE: 注意事项
// 配合 IDE 显示待办列表
6. 注释最佳实践
1. 公共 API 写文档注释(生成文档)
2. 解释"为什么"而非"是什么"
3. 复杂算法注明思路
4. 注释要随代码更新
5. 使用 markdown 格式增强可读性
Rust 函数
Rust 函数
函数是 Rust 代码组织的基本单元,支持模式匹配参数、泛型与闭包。
1. 定义与调用
fn main() {
greet("张三");
let sum = add(3, 5);
println!("sum = {}", sum);
}
// 无返回值
fn greet(name: &str) {
println!("你好, {}", name);
}
// 有返回值(-> 类型)
fn add(a: i32, b: i32) -> i32 {
a + b // 表达式作为返回值(无分号)
}
2. 返回值规则
fn f1() -> i32 {
return 42; // 显式 return(早退用)
}
fn f2() -> i32 {
42 // 隐式返回(最后一个表达式)
}
fn f3() -> i32 {
42; // 错误!有分号是语句,返回 ()
}
// 无返回值
fn f4() {} // 返回 () 单元类型
3. 多个返回值(元组)
fn min_max(nums: &[i32]) -> (i32, i32) {
let mut min = nums[0];
let mut max = nums[0];
for &n in nums {
if n < min { min = n; }
if n > max { max = n; }
}
(min, max)
}
fn main() {
let (min, max) = min_max(&[3, 1, 4, 1, 5]);
println!("min={} max={}", min, max); // min=1 max=5
}
4. 泛型函数
// T 可以是任意类型
fn largest(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let nums = vec![1, 5, 3, 9, 2];
println!("{}", largest(&nums)); // 9
let chars = vec!['a', 'z', 'm'];
println!("{}", largest(&chars)); // z
}
5. 高阶函数(函数指针)
fn apply_twice(f: fn(i32) -> i32, x: i32) -> i32 {
f(f(x))
}
fn double(x: i32) -> i32 { x * 2 }
fn main() {
let result = apply_twice(double, 5);
println!("{}", result); // 20
}
6. 闭包(匿名函数)
fn main() {
// 闭包: || 语法
let add = |a: i32, b: i32| a + b;
println!("{}", add(1, 2)); // 3
// 捕获外部变量
let factor = 10;
let multiply = |x| x * factor; // 捕获 factor
println!("{}", multiply(5)); // 50
// 高阶用法
let nums = vec![1, 2, 3, 4];
let doubled: Vec = nums.iter().map(|x| x * 2).collect();
let evens: Vec = nums.iter().filter(|x| **x % 2 == 0).collect();
}
Rust 条件语句
Rust 条件语句
Rust 的 if 是表达式(有值),支持多种条件结构。
1. if / else if / else
fn main() {
let score = 85;
if score >= 90 {
println!("优秀");
} else if score >= 60 {
println!("及格");
} else {
println!("不及格");
}
}
2. if 作为表达式
let number = 7;
let result = if number % 2 == 0 { "偶数" } else { "奇数" };
println!("{}", result); // 奇数
// 类型必须一致
let x = if true { 10 } else { 20 };
// let y = if true { 10 } else { "str" }; // 错误!类型不同
3. 循环中的条件
// while 循环
let mut count = 0;
while count < 5 {
println!("count = {}", count);
count += 1;
}
// loop 无限循环(用 break 退出)
let mut i = 0;
loop {
i += 1;
if i > 3 { break; }
println!("i = {}", i);
}
// loop 可返回值
let mut j = 0;
let result = loop {
j += 1;
if j == 5 {
break j * 2; // 返回 10
}
};
println!("result = {}", result); // 10
4. for 循环
// 范围
for i in 1..5 { // 1,2,3,4
println!("{}", i);
}
for i in 1..=5 { // 1,2,3,4,5(含结尾)
println!("{}", i);
}
// 数组/向量
let arr = [10, 20, 30];
for v in arr {
println!("{}", v);
}
// 带索引
for (i, v) in arr.iter().enumerate() {
println!("arr[{}] = {}", i, v);
}
// 反向
for i in (1..=5).rev() {
println!("{}", i); // 5 4 3 2 1
}
5. break / continue / 标签
// break: 退出循环
// continue: 跳过本次
// 循环标签(嵌套循环跳出外层)
'outer: for i in 1..=3 {
for j in 1..=3 {
if j == 2 { break 'outer; }
println!("({}, {})", i, j); // 只输出 (1, 1)
}
}
6. match 匹配(最强条件)
let number = 13;
match number {
1 => println!("一"),
2 | 3 | 5 | 7 => println!("质数"),
10..=20 => println!("10 到 20 之间"), // 范围
_ => println!("其他"), // 兜底
}
// match 是表达式,可赋值
let grade = match score {
90..=100 => "A",
80..=89 => "B",
70..=79 => "C",
60..=69 => "D",
_ => "F",
};
Rust 循环
Rust 循环
Rust 提供 loop、while、for 三种循环,各有适用场景。
1. loop(无限循环)
// loop 会一直执行,直到 break
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break 可带值
}
};
println!("result = {}", result); // 20
// 服务器/监听器常见模式
loop {
// 处理请求...
// 检查退出条件
if should_exit { break; }
}
2. while(条件循环)
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("发射!");
// while let(模式匹配循环)
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{}", top); // 3 2 1
}
3. for(集合遍历)
// for 是最常用的循环
let names = ["Alice", "Bob", "Carol"];
for name in names {
println!("{}", name);
}
// 范围
for n in 1..=100 {
// 求 1..100 之和
}
// 对迭代器
let numbers = vec![5, 3, 8, 1];
for n in numbers.iter() {
println!("{}", n);
}
// 注意: numbers 仍可用(借用)
// for n in numbers 则所有权移动
4. 循环中的控制流
// break: 退出
// continue: 跳过本次
for i in 1..=10 {
if i % 2 == 0 {
continue; // 跳过偶数
}
if i > 7 {
break; // 大于 7 退出
}
println!("{}", i); // 1 3 5 7
}
5. 循环标签与嵌套
'outer: for i in 1..=5 {
println!("外层: {}", i);
'inner: for j in 1..=5 {
if j == 3 {
continue 'outer; // 跳到外层下一次
}
if i * j > 10 {
break 'outer; // 退出外层
}
println!(" 内层: {}", j);
}
}
6. 迭代器方法(推荐风格)
// Rust 风格: 多用迭代器方法替代手写循环
let nums = vec![1, 2, 3, 4, 5];
// map: 转换
let doubled: Vec<_> = nums.iter().map(|x| x * 2).collect();
// filter: 过滤
let evens: Vec<_> = nums.iter().filter(|x| **x % 2 == 0).collect();
// sum / product
let sum: i32 = nums.iter().sum();
// fold: 聚合
let total: i32 = nums.iter().fold(0, |acc, x| acc + x);
// chain: 链式
let result: Vec<_> = nums.iter()
.filter(|x| **x > 2)
.map(|x| x * 10)
.collect(); // [30, 40, 50]
Rust 迭代器
Rust 迭代器
迭代器是 Rust 集合处理的核心抽象,配合闭包可以写出简洁高效的数据处理代码。
1. 什么是迭代器
// 迭代器惰性: 只在调用消费方法时才执行
let nums = vec![1, 2, 3, 4, 5];
let iter = nums.iter(); // 创建迭代器,尚未执行
// 消费: for 循环
for n in iter {
println!("{}", n);
}
2. 三种 iter
let v = vec![1, 2, 3];
v.iter() // &T 借用迭代(&1, &2, &3)
v.iter_mut() // &mut T 可变借用
v.into_iter() // T 所有权(移动)
// 数组
let arr = [1, 2, 3];
arr.iter().for_each(|x| println!("{}", x));
3. 常用适配器(转换)
let nums = vec![1, 2, 3, 4, 5, 6];
// map: 变换
let squares: Vec = nums.iter().map(|x| x * x).collect();
// [1, 4, 9, 16, 25, 36]
// filter: 过滤
let evens: Vec<&i32> = nums.iter().filter(|x| **x % 2 == 0).collect();
// [2, 4, 6]
// take / skip
let first3: Vec<&i32> = nums.iter().take(3).collect(); // [1,2,3]
let after2: Vec<&i32> = nums.iter().skip(2).collect(); // [3,4,5,6]
// enumerate: 带索引
for (i, x) in nums.iter().enumerate() {
println!("nums[{}] = {}", i, x);
}
// zip: 配对
let a = [1, 2, 3];
let b = ["a", "b", "c"];
for (x, y) in a.iter().zip(b.iter()) {
println!("{} -> {}", x, y);
}
4. 消费方法
let nums = vec![1, 2, 3, 4, 5];
let count = nums.iter().count(); // 5
let sum: i32 = nums.iter().sum(); // 15
let product: i32 = nums.iter().product(); // 120
let max = nums.iter().max(); // Some(&5)
let min = nums.iter().min(); // Some(&1)
let any = nums.iter().any(|x| x > 4); // true
let all = nums.iter().all(|x| x > 0); // true
// find / position
let found = nums.iter().find(|x| **x == 3); // Some(&3)
let pos = nums.iter().position(|x| *x == 3); // Some(2)
// fold: 通用聚合
let total = nums.iter().fold(0, |acc, x| acc + x); // 15
// collect: 收集到容器
let doubled: Vec = nums.iter().map(|x| x * 2).collect();
let filtered: Vec = nums.into_iter().filter(|x| x % 2 == 0).collect();
5. 链式组合示例
let nums = vec![1, 2, 3, 4, 5, 6, 7, 8];
// 过滤偶数 → 平方 → 取前 3 → 求和
let result: i32 = nums.iter()
.filter(|x| **x % 2 == 0) // [2,4,6,8]
.map(|x| x * x) // [4,16,36,64]
.take(3) // [4,16,36]
.sum(); // 56
println!("{}", result); // 56
6. 自定义迭代器
// 实现 Iterator trait
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.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
fn main() {
let sum: u32 = Counter::new()
.map(|x| x * 2)
.sum();
println!("{}", sum); // 30
}
Rust 闭包
Rust 闭包(Closure)
闭包是能捕获环境变量的匿名函数,Rust 中通过所有权规则严格管理闭包的捕获方式。
1. 闭包语法
// 完整形式
let add = |a: i32, b: i32| -> i32 { a + b };
// 类型可推断
let add = |a, b| a + b;
println!("{}", add(1, 2)); // 3
// 无参数
let hello = || println!("Hello!");
hello();
// 闭包调用与函数类似
let square = |x: i32| x * x;
println!("{}", square(5)); // 25
2. 捕获环境变量
let factor = 3;
let multiply = |x| x * factor; // 捕获 factor
println!("{}", multiply(10)); // 30
// 捕获方式由编译器自动选择:
// FnOnce: 按值捕获(消耗所有权)
// FnMut: 可变借用(可修改)
// Fn: 不可变借用(只读)
let mut count = 0;
let mut increment = || {
count += 1; // 可变借用 → FnMut
};
increment();
increment();
println!("count = {}", count); // 2
3. move 关键字
// move 强制按值捕获(所有权转移)
let s = String::from("hello");
let closure = move || {
println!("{}", s); // s 的所有权被移动进闭包
};
closure();
// println!("{}", s); // 错误!s 已被移动
// 常用于线程
use std::thread;
let data = String::from("thread data");
thread::spawn(move || {
println!("{}", data);
}).join().unwrap();
4. 作为参数
// 泛型闭包参数
fn apply(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32, // 约束: 闭包类型
{
f(x)
}
// Fn / FnMut / FnOnce 三种 trait
fn apply_once(f: F) {
f();
}
fn apply_mut(mut f: F) {
f();
}
fn apply_fn(f: F) {
f();
}
5. 返回闭包
// 返回闭包: 用 impl Fn 或 Box
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor // move 捕获 factor
}
fn main() {
let times3 = make_multiplier(3);
println!("{}", times3(10)); // 30
}
// 不同闭包类型需用 Box
fn make_adder(add: i32) -> Box i32> {
Box::new(move |x| x + add)
}
6. 与迭代器配合(经典用法)
let nums = vec![1, 2, 3, 4, 5];
let result: Vec = nums.iter()
.map(|x| x * x) // 闭包
.filter(|x| x % 2 == 0) // 闭包
.collect();
println!("{:?}", result); // [4, 16]
// 排序闭包
let mut scores = vec![85, 92, 78, 90];
scores.sort_by(|a, b| b.cmp(a)); // 降序
println!("{:?}", scores); // [92, 90, 85, 78]
Rust 所有权
Rust 所有权(Ownership)
所有权是 Rust 最核心的概念,它让内存安全无需垃圾回收即可实现。
1. 所有权规则
1. 每个值都有一个所有者(owner)
2. 同一时刻只有一个所有者
3. 所有者离开作用域,值被自动释放(drop)
{
let s = String::from("hello"); // s 拥有字符串
// 使用 s
} // 作用域结束,s 被释放,内存自动回收
// s 在这里不可用
2. 移动(Move)
let s1 = String::from("hello");
let s2 = s1; // 所有权从 s1 移动到 s2(浅拷贝+失效)
// println!("{}", s1); // 错误!s1 已失效
println!("{}", s2); // hello
// 不是拷贝!字符串数据只存在一份,所有权转移
// 避免双重释放(与 C++ move 语义类似)
3. 克隆(Clone)
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝,两份数据
println!("{} {}", s1, s2); // hello hello
// 拷贝(Copy)类型: 小数据类型直接复制
// i32/f64/bool/char/元组(内部都是 Copy)
let x = 5;
let y = x; // Copy,不需要 clone
println!("{} {}", x, y); // 5 5
4. 函数参数的所有权
fn takes_ownership(s: String) {
println!("{}", s);
} // s 在这里被释放
fn makes_copy(x: i32) {
println!("{}", x);
}
fn main() {
let s = String::from("hello");
takes_ownership(s); // 所有权移入函数
// println!("{}", s); // 错误!s 已移动
let n = 5;
makes_copy(n); // Copy 类型,仍可用
println!("{}", n); // 5
}
5. 返回值转移所有权
fn gives_back() -> String {
String::from("返回值")
}
fn takes_and_gives(s: String) -> String {
s // 返回,所有权转移回去
}
fn main() {
let s1 = gives_back();
let s2 = takes_and_gives(s1);
println!("{}", s2);
// s1 已移动到函数又移回来,此处 s1 失效
}
6. 引用与借用(解决移动问题)
fn calculate_len(s: &String) -> usize { // & 借用
s.len()
} // s 是借用,不释放
fn main() {
let s = String::from("hello");
let len = calculate_len(&s); // 传引用
println!("{} 长度 {}", s, len); // s 仍可用
}
// 规则:
// 1. 任意时刻要么一个可变引用,要么多个不可变引用
// 2. 引用必须始终有效(不悬垂)
let mut s = String::from("hello");
let r1 = &s; // 不可变借用
// let r2 = &mut s; // 错误!已有不可变借用
println!("{}", r1);
7. 引用的规则
let mut s = String::from("hello");
// 多个不可变借用: OK
let r1 = &s;
let r2 = &s;
println!("{} {}", r1, r2);
// 可变借用: 同一时刻只能一个
let r3 = &mut s;
r3.push_str(" world");
// 借用结束后才能再借用
println!("{}", s); // hello world
// 悬垂引用: 编译器拒绝
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // 错误!s 离开作用域被释放
// }
Rust Slice(切片)类型
Rust Slice(切片)类型
切片(Slice)是对集合连续部分的不拥有所有权的引用,安全且高效。
1. 什么是切片
// 切片 = 指向集合某段的引用
// 类型: &[T](数组切片)、&str(字符串切片)
let arr = [1, 2, 3, 4, 5];
let slice: &[i32] = &arr[1..4]; // [2, 3, 4]
// 不拥有 arr,只是借用其中一段
2. 字符串切片
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"
let whole = &s[..]; // "hello world"
// 切片必须是 UTF-8 边界
// let bad = &s[0..4]; // 若边界在汉字中间会 panic
// 字符串字面量本身是切片
let lit: &str = "hello"; // &str 类型
3. 切片语法
let arr = [1, 2, 3, 4, 5];
&arr[..] // 全部 [1,2,3,4,5]
&arr[1..] // [2,3,4,5]
&arr[..3] // [1,2,3]
&arr[1..3] // [2,3]
&arr[1..=3] // [2,3,4] 含结尾
// 空切片
&arr[2..2] // []
4. 切片 vs 数组 vs String
// 数组: 拥有数据,固定长度
let arr: [i32; 5] = [1, 2, 3, 4, 5];
// 切片: 借用数据,不拥有
let slice: &[i32] = &arr[1..3];
// String: 拥有,可增长
let mut s = String::from("hello");
s.push('!');
// &str: 借用字符串,不可变
let s2: &str = &s;
// 参数设计: 能用 &str 就用 &str(更通用)
fn print_str(s: &str) {
println!("{}", s);
}
// 可以传 &String 和 &str
5. 函数中使用切片
fn sum(slice: &[i32]) -> i32 {
slice.iter().sum()
}
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let nums = [1, 2, 3, 4, 5];
println!("{}", sum(&nums[1..4])); // 9
let s = String::from("hello world");
println!("{}", first_word(&s)); // hello
}
6. 可变切片
let mut arr = [1, 2, 3, 4, 5];
let slice = &mut arr[1..4]; // 可变切片
slice[0] = 99;
println!("{:?}", arr); // [1, 99, 3, 4, 5]
7. 切片的安全保证
// 越界在编译/运行时检查
// 借用规则保证: 切片存在时,原集合不可变/不可整体移动
let mut v = vec![1, 2, 3];
let slice = &v[..2];
// v.push(4); // 错误!切片借用中,不能修改 v
println!("{:?}", slice);
Rust 结构体
Rust 结构体(Struct)
结构体是 Rust 自定义数据类型的主要方式,用于组织相关联的数据。
1. 定义与实例化
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
let user = User {
email: String::from("a@example.com"),
username: String::from("zhangsan"),
active: true,
sign_in_count: 1,
};
println!("{}", user.username);
}
2. 可变实例与更新
let mut user = User {
email: String::from("a@example.com"),
username: String::from("zhangsan"),
active: true,
sign_in_count: 1,
};
user.email = String::from("b@example.com");
// 结构体更新语法(..user)
let user2 = User {
email: String::from("c@example.com"),
..user // 其余字段从 user 复制
};
// 注意: user 的 String 字段被移动后 user 不可用
3. 构造函数模式
struct User {
username: String,
email: String,
active: bool,
}
impl User {
fn new(username: String, email: String) -> User {
User {
username,
email,
active: true,
}
}
}
let u = User::new(
String::from("zhang"),
String::from("z@example.com")
);
4. 元组结构体(Tuple Struct)
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// 访问
println!("{}", black.0); // 0
// 解构
let Color(r, g, b) = black;
println!("{} {} {}", r, g, b);
5. 单元结构体(Unit Struct)
// 无字段,用于标记/实现 trait
struct Marker;
impl Marker {
fn new() -> Marker {
Marker
}
}
let m = Marker;
let m2 = Marker::new();
6. 方法(Methods)
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 方法: &self 借用
fn area(&self) -> u32 {
self.width * self.height
}
// 可变方法: &mut self
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
// 关联函数(无 self): 类似静态方法
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
fn main() {
let mut rect = Rectangle { width: 3, height: 4 };
println!("面积: {}", rect.area()); // 12
rect.scale(2);
println!("面积: {}", rect.area()); // 48
let sq = Rectangle::square(5);
println!("{}", sq.area()); // 25
}
7. 派生 Trait
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone();
println!("{:?}", p1); // Debug 输出
println!("{}", p1 == p2); // PartialEq -> true
}
Rust 枚举类
Rust 枚举类(Enum)
Rust 枚举比 C 语言强大得多,可以携带数据,是表达"可能性"的首选。
1. 基本枚举
enum Direction {
North,
South,
East,
West,
}
fn move_to(dir: Direction) {
match dir {
Direction::North => println!("向北"),
Direction::South => println!("向南"),
Direction::East => println!("向东"),
Direction::West => println!("向西"),
}
}
fn main() {
let d = Direction::North;
move_to(d);
}
2. 携带数据(关联值)
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 命名数据
Write(String), // 单个数据
ChangeColor(u8, u8, u8), // 元组数据
}
fn process(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);
}
}
}
3. Option 枚举(核心)
// Option: 有值(Some)或无值(None)
// 消除空指针问题
let some_number = Some(5);
let some_string = Some("hello");
let none: Option = None;
// 必须处理 None,不能直接当数值用
fn double(x: Option) -> i32 {
match x {
Some(n) => n * 2,
None => 0,
}
}
// 常用方法
let x = Some(10);
println!("{}", x.unwrap_or(0)); // 10
println!("{}", x.unwrap_or_default()); // 10
if let Some(v) = x {
println!("值是 {}", v);
}
let y: Option = None;
println!("{}", y.unwrap_or(42)); // 42
4. match 的穷尽性
// match 必须覆盖所有情况(穷尽性检查)
let x = Some(5);
match x {
Some(n) => println!("{}", n),
None => println!("无值"),
// 如果漏掉 None,编译错误!
}
// _ 通配符处理剩余情况
let n = 3;
match n {
1 => println!("一"),
2 => println!("二"),
_ => println!("其他"), // 兜底
}
5. if let 简写
// 只关心一种情况的简写
let config_max = Some(3u8);
match config_max {
Some(max) => println!("最大值 {}", max),
_ => (),
}
// 等价写法
if let Some(max) = config_max {
println!("最大值 {}", max);
}
// while let
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{}", top);
}
6. 方法(match self)
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
impl Coin {
fn value_in_cents(&self) -> u32 {
match self {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
}
fn main() {
let coin = Coin::Quarter;
println!("{} 分", coin.value_in_cents()); // 25
}
7. 递归枚举
// 链表等递归结构需 Box 包装(间接引用)
enum List {
Cons(i32, Box),
Nil,
}
fn main() {
let list = List::Cons(
1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("递归枚举(链表)创建成功");
}
Rust 组织管理
Rust 组织管理(模块系统)
Rust 用模块(module)、包(package)、crate 组织代码,实现清晰的代码结构。
1. 基本概念
Package(包): Cargo 管理的项目(一个 Cargo.toml)
Crate(箱子): 编译单元(二进制或库)
Module(模块): 组织代码的命名空间
cargo new myapp
# ├── src/main.rs 二进制 crate
# └── Cargo.toml
cargo new --lib mylib
# ├── src/lib.rs 库 crate
# └── Cargo.toml
2. 模块定义
// src/main.rs
mod math { // 定义模块
pub fn add(a: i32, b: i32) -> i32 { // pub 公开
a + b
}
fn secret() { // 私有(默认)
println!("内部函数");
}
pub mod utils { // 嵌套模块
pub fn double(x: i32) -> i32 {
x * 2
}
}
}
fn main() {
println!("{}", math::add(1, 2)); // 3
println!("{}", math::utils::double(4)); // 8
// math::secret(); // 错误!私有
}
3. 文件模块
// src/main.rs
mod math; // 从 math.rs 加载模块
// src/math.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }
// 或目录方式
// src/math/mod.rs(旧风格)
// src/math/utils.rs
// 目录 + 文件(推荐)
// src/math/mod.rs 或 src/math.rs
// ├── math.rs
// └── math/
// └── utils.rs → mod utils;
// 现代风格(2021 edition):
// src/math.rs + src/math/utils.rs
4. use 引入
mod math;
// 引入
use math::add;
use math::utils::double;
use std::collections::HashMap;
// 相对/绝对路径
use crate::math::add; // 绝对(crate 根)
use self::math::add; // 相对(self)
// 别名
use math::add as sum;
// 通配
use math::*;
fn main() {
println!("{}", add(1, 2));
let mut map = HashMap::new();
map.insert("k", 1);
}
5. pub use 再导出
// 库的入口统一导出
// src/lib.rs
mod internal;
pub use internal::PublicStruct; // 再导出
// 使用方
// use mylib::PublicStruct; // 不用知道内部路径
6. 可见性
pub 公开(所有模块可见)
pub(crate) crate 内部可见
pub(super) 父模块可见
pub(in path) 指定路径可见
(默认) 当前模块及子模块可见
pub struct User {
pub name: String, // 字段公开
age: u8, // 字段私有
}
7. 依赖外部 crate
// Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }
rand = "0.8"
// main.rs
use rand::Rng;
fn main() {
let n: u32 = rand::thread_rng().gen_range(1..=100);
println!("{}", n);
}
Rust 错误处理
Rust 错误处理
Rust 没有异常机制,使用 Result 与 Option 显式处理错误,让错误处理强制、清晰、安全。
1. Result 枚举
// Result: Ok(T) 成功 / Err(E) 失败
enum Result {
Ok(T),
Err(E),
}
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("打开失败: {:?}", error),
};
}
2. 常用错误处理方式
// 1. expect: 失败直接 panic
let f = File::open("a.txt").expect("文件不存在");
// 2. unwrap: 失败 panic(简洁但鲁莽)
let f = File::open("a.txt").unwrap();
// 3. 处理错误
let f = match File::open("a.txt") {
Ok(file) => file,
Err(e) => {
eprintln!("错误: {}", e);
return; // 或返回 Err
}
};
3. ? 运算符(推荐)
// ? 遇到 Err 自动返回,函数必须返回 Result
use std::fs::File;
use std::io::{self, Read};
fn read_username() -> Result {
let mut f = File::open("user.txt")?; // Err 直接返回
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() -> Result<(), io::Error> {
let name = read_username()?;
println!("用户: {}", name);
Ok(())
}
// ? 可自动转换错误类型(From trait)
fn read_file() -> Result> {
let content = std::fs::read_to_string("a.txt")?;
let n: i32 = content.trim().parse()?; // 错误自动转换
Ok(format!("{}", n))
}
4. 自定义错误类型
use std::fmt;
#[derive(Debug)]
enum MyError {
NotFound(String),
InvalidInput(String),
Io(std::io::Error),
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MyError::NotFound(s) => write!(f, "未找到: {}", s),
MyError::InvalidInput(s) => write!(f, "无效输入: {}", s),
MyError::Io(e) => write!(f, "IO 错误: {}", e),
}
}
}
impl std::error::Error for MyError {}
// 错误转换
impl From for MyError {
fn from(e: std::io::Error) -> Self {
MyError::Io(e)
}
}
fn read_config() -> Result {
let content = std::fs::read_to_string("config.toml")?; // 自动转换
Ok(content)
}
5. 优雅错误处理库
// anyhow: 应用层,任意错误
// thiserror: 库层,自定义错误
// Cargo.toml
// anyhow = "1"
use anyhow::{Context, Result};
fn main() -> Result<()> {
let content = std::fs::read_to_string("config.toml")
.context("读取配置文件失败")?; // 添加上下文
println!("{}", content);
Ok(())
}
// thiserror 示例
use thiserror::Error;
#[derive(Error, Debug)]
enum AppError {
#[error("用户 {0} 不存在")]
UserNotFound(String),
#[error("网络错误")]
Network,
}
6. 错误处理最佳实践
1. 库代码: 返回自定义错误类型(Result)
2. 应用代码: anyhow 简化处理
3. 简单脚本: unwrap/expect(快速原型)
4. 用户输入错误: 正常处理,不 panic
5. panic 留给: 不可恢复的错误(数组越界等)
6. 用 context 增加错误上下文信息
Rust 泛型与特性
Rust 泛型与特性(Trait)
泛型让代码适用于多种类型,trait 定义共享行为,两者结合构成 Rust 抽象的基础。
1. 泛型函数
// 返回两个数中较大者
fn max(a: T, b: T) -> T {
if a > b { a } else { b }
}
fn main() {
println!("{}", max(1, 2)); // i32
println!("{}", max(1.5, 0.5)); // f64
println!("{}", max('a', 'z')); // char
}
// 泛型结构体
struct Point {
x: T,
y: T,
}
// 泛型枚举
enum Result {
Ok(T),
Err(E),
}
2. Trait 定义
// trait 定义共享行为(类似接口)
trait Summary {
fn summarize(&self) -> String;
// 带默认实现的方法
fn author(&self) -> String {
String::from("未知作者")
}
}
struct Article {
title: String,
author: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("《{}》by {}", self.title, self.author)
}
}
fn main() {
let article = Article {
title: String::from("Rust 教程"),
author: String::from("张三"),
};
println!("{}", article.summarize());
println!("{}", article.author()); // 默认实现
}
3. Trait 作为参数
// 1. impl Trait 语法
fn notify(item: &impl Summary) {
println!("通知: {}", item.summarize());
}
// 2. 泛型约束(Trait Bound)
fn notify2(item: &T) {
println!("通知: {}", item.summarize());
}
// 3. 多个约束
fn notify3(item: &T) {}
// 4. where 子句(清晰)
fn notify4(a: &T, b: &U)
where
T: Summary + Clone,
U: Summary + Debug,
{}
// 5. 返回 trait
fn returns_summarizable() -> impl Summary {
Article { title: String::from("R"), author: String::from("B") }
}
4. 常用标准 trait
Clone 复制(深拷贝) #[derive(Clone)]
Copy 位拷贝 #[derive(Copy, Clone)]
Debug 调试输出 {:?} #[derive(Debug)]
PartialEq 相等比较 #[derive(PartialEq)]
Eq 全等
PartialOrd 比较
Ord 全序
Display 显示输出 {}
Iterator 迭代器
Into/From 类型转换
Default 默认值
// 示例
#[derive(Debug, Clone, PartialEq)]
struct User {
name: String,
age: u8,
}
5. 常用泛型组合
// Option 与 Result
fn safe_divide(a: f64, b: f64) -> Result {
if b == 0.0 {
Err(String::from("不能除以零"))
} else {
Ok(a / b)
}
}
fn main() {
let r = safe_divide(10.0, 2.0);
match r {
Ok(v) => println!("结果 {}", v),
Err(e) => println!("错误 {}", e),
}
}
// Vec、HashMap 等标准容器都是泛型
6. Trait 对象
// 动态分发: 运行时决定具体类型
// 需要 Box
trait Draw {
fn draw(&self);
}
struct Circle;
impl Draw for Circle {
fn draw(&self) { println!("画圆"); }
}
struct Square;
impl Draw for Square {
fn draw(&self) { println!("画方"); }
}
fn main() {
let shapes: Vec> = vec![
Box::new(Circle),
Box::new(Square),
];
for s in shapes {
s.draw();
}
}
// 静态分发(泛型) vs 动态分发(dyn)
// 泛型: 编译期展开,快,但代码膨胀
// dyn: 运行时查找,灵活,小开销
Rust 生命周期
Rust 生命周期(Lifetime)
生命周期是引用有效范围的标注,是 Rust 防止悬垂引用的机制。
1. 为什么需要生命周期
// 引用必须始终有效,否则悬垂
fn main() {
let r;
{
let x = 5;
r = &x; // 错误!x 在作用域结束后被释放
}
println!("{}", r);
}
// 编译器通过生命周期检查引用有效性
2. 生命周期标注语法
// 语法: &'a T(生命周期 'a 的引用)
// 泛型生命周期参数
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("long");
let s2 = String::from("short");
let result = longest(&s1, &s2);
// 返回值生命周期取两者较短者
println!("{}", result);
}
3. 生命周期省略规则
// 编译器自动推断,不必显式写
// 规则:
// 1. 每个输入引用分配生命周期
// 2. 只有一个输入生命周期 → 输出用它
// 3. 多个输入,其中一个是 &self → 输出用 self 的
// 不需要标注(单参数)
fn first_word(s: &str) -> &str {
&s[..]
}
// 需要标注(多参数,输出与输入关联)
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
4. 结构体中的生命周期
// 结构体持有引用时需要标注
struct Excerpt<'a> {
part: &'a str, // 引用必须标注生命周期
}
impl<'a> Excerpt<'a> {
fn level(&self) -> i32 { 3 }
}
fn main() {
let novel = String::from("第一章 内容...");
let first = novel.split('.').next().unwrap();
let excerpt = Excerpt { part: first };
println!("{}", excerpt.part);
}
5. 生命周期与泛型、trait 结合
fn longest_with_announcement<'a, T>(
x: &'a str,
y: &'a str,
ann: T,
) -> &'a str
where
T: std::fmt::Display,
{
println!("公告: {}", ann);
if x.len() > y.len() { x } else { y }
}
6. 静态生命周期 'static
// 'static: 整个程序运行期间有效
// 字符串字面量就是 'static
let s: &'static str = "hello"; // 字面量是 'static
// 动态字符串转 'static(内存泄漏,慎用)
let leak: &'static str = Box::leak(
String::from("leak").into_boxed_str());
// 常配合 trait 对象
fn get_display() -> Box {
Box::new(String::from("hello"))
}
7. 生命周期最佳实践
1. 大多数情况编译器自动处理(省略规则)
2. 函数返回引用与输入相关时标注
3. 结构体持有引用时标注
4. 实在复杂,考虑用所有权代替引用
5. 用 &'static 前想清楚是否真的需要
// 提示: 如果总是为生命周期头疼,
// 可以考虑返回 String 而不是 &str(简单直观)
Rust 文件与 IO
Rust 文件与 IO
Rust 标准库提供完整的文件读写与 IO 操作能力。
1. 读取文件
use std::fs;
fn main() -> std::io::Result<()> {
// 一次性读取整个文件
let content = fs::read_to_string("data.txt")?;
println!("{}", content);
// 读取为字节
let bytes = fs::read("data.bin")?;
println!("{} 字节", bytes.len());
Ok(())
}
2. 写入文件
use std::fs;
fn main() -> std::io::Result<()> {
// 覆盖写入
fs::write("out.txt", "Hello Rust")?;
// 追加写入
use std::io::Write;
let mut file = fs::OpenOptions::new()
.append(true) // 追加模式
.create(true) // 不存在则创建
.open("log.txt")?;
file.write_all(b"新的一行
")?;
Ok(())
}
3. 逐行读取
use std::io::{self, BufRead, BufReader};
use std::fs::File;
fn main() -> io::Result<()> {
let file = File::open("data.txt")?;
let reader = BufReader::new(file);
for (i, line) in reader.lines().enumerate() {
let line = line?;
println!("{}: {}", i + 1, line);
}
Ok(())
}
4. 目录操作
use std::fs;
fn main() -> std::io::Result<()> {
// 创建目录
fs::create_dir("new_dir")?;
fs::create_dir_all("a/b/c")?; // 递归创建
// 读取目录
for entry in fs::read_dir(".")? {
let entry = entry?;
let path = entry.path();
println!("{:?}", path);
}
// 删除
fs::remove_dir("empty_dir")?;
fs::remove_dir_all("a")?; // 递归删除
// 文件操作
fs::copy("src.txt", "dst.txt")?;
fs::rename("old.txt", "new.txt")?;
fs::remove_file("temp.txt")?;
Ok(())
}
5. 错误处理
use std::fs;
fn main() {
match fs::read_to_string("missing.txt") {
Ok(content) => println!("{}", content),
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => {
println!("文件不存在");
}
std::io::ErrorKind::PermissionDenied => {
println!("没有权限");
}
_ => println!("其他错误: {}", e),
},
}
}
6. 路径处理(Path)
use std::path::Path;
fn main() {
let path = Path::new("/tmp/data/file.txt");
println!("exists: {}", path.exists());
println!("is_file: {}", path.is_file());
println!("is_dir: {}", path.is_dir());
println!("file_name: {:?}", path.file_name());
println!("extension: {:?}", path.extension());
println!("parent: {:?}", path.parent());
// 拼接
let new = path.join("sub").join("x.log");
println!("{:?}", new);
}
7. 文本处理常用操作
use std::fs;
fn main() -> std::io::Result<()> {
let content = fs::read_to_string("data.txt")?;
// 按行处理
let mut lines: Vec<&str> = content.lines().collect();
// 过滤空行
lines.retain(|l| !l.trim().is_empty());
// 去重
lines.sort();
lines.dedup();
// 写入
fs::write("clean.txt", lines.join("
"))?;
Ok(())
}
Rust 集合与字符串
Rust 集合与字符串
标准库的 Vec、HashMap、HashSet、String 是日常开发的主力容器。
1. Vec 向量
fn main() {
// 创建
let mut v: Vec = Vec::new();
let mut v2 = vec![1, 2, 3]; // 宏创建
// 添加/删除
v.push(10);
v.push(20);
v.pop(); // 移除末尾 -> Some(20)
v.insert(0, 5); // 插入开头
// 访问
let first = &v[0]; // 索引(越界 panic)
let second = v.get(1); // Option<&i32>
println!("{:?}", second);
// 遍历
for x in &v { println!("{}", x); }
for x in &mut v2 { *x += 1; }
// 常用方法
v.len();
v.is_empty();
v.contains(&10);
v.sort();
v.reverse();
v.iter().map(|x| x * 2).collect::>();
}
2. HashMap 哈希映射
use std::collections::HashMap;
fn main() {
// 创建
let mut scores = HashMap::new();
scores.insert(String::from("数学"), 90);
scores.insert(String::from("语文"), 85);
// 读取
let math = scores.get("数学"); // Option<&i32>
println!("{:?}", math); // Some(90)
// 不存在则插入(entry API)
scores.entry(String::from("英语"))
.or_insert(75);
// 修改
*scores.get_mut("数学").unwrap() = 95;
// 遍历
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// 统计单词出现次数
let text = "apple banana apple";
let mut count = HashMap::new();
for word in text.split_whitespace() {
*count.entry(word).or_insert(0) += 1;
}
println!("{:?}", count); // {"apple": 2, "banana": 1}
}
3. HashSet 哈希集合
use std::collections::HashSet;
fn main() {
let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(1); // 重复,忽略
println!("{:?}", set); // {1, 2}
// 判断
set.contains(&1); // true
// 集合运算
let a: HashSet<_> = [1, 2, 3].iter().collect();
let b: HashSet<_> = [3, 4, 5].iter().collect();
let union: HashSet<_> = a.union(&b).collect(); // 并
let intersection: HashSet<_> = a.intersection(&b).collect(); // 交
let difference: HashSet<_> = a.difference(&b).collect(); // 差
// 去重
let nums = vec![1, 2, 2, 3, 3, 3];
let unique: HashSet = nums.into_iter().collect();
println!("{:?}", unique); // {1, 2, 3}
}
4. String 与 &str
fn main() {
// 创建
let mut s = String::new();
let s2 = String::from("hello");
let s3 = "world".to_string();
// 追加
s.push('!'); // 字符
s.push_str("hello"); // 字符串
println!("{}", s); // !hello
// 拼接
let a = String::from("Hello, ");
let b = String::from("World!");
let c = a + &b; // 注意 &b
println!("{}", c); // Hello, World!
// 格式化
let name = "张三";
let greeting = format!("你好, {}!", name);
// 切片(按字节,注意边界)
let s4 = "Hello";
let slice = &s4[0..3]; // "Hel"
// 遍历
for c in s4.chars() { } // 字符
for b in s4.bytes() { } // 字节
// 查找/替换
let text = "hello world";
text.contains("world"); // true
text.replace("world", "rust"); // "hello rust"
text.to_uppercase(); // "HELLO WORLD"
text.trim(); // 去空白
}
5. 集合转换
let v = vec![1, 2, 3];
// Vec -> String
let joined = v.iter()
.map(|x| x.to_string())
.collect::>()
.join(","); // "1,2,3"
// String -> Vec
let parts: Vec<&str> = "a,b,c".split(',').collect();
// Vec -> HashMap
let pairs = vec![(1, "a"), (2, "b")];
let map: HashMap<_, _> = pairs.into_iter().collect();
6. 常用技巧
// 循环引用集合的迭代
let v = vec![1, 2, 3];
for &x in &v { println!("{}", x); } // 解引用
// 排序自定义类型
let mut users = vec![(3, "c"), (1, "a"), (2, "b")];
users.sort(); // 按元组排序
// 最大值/最小值
let max = v.iter().max().unwrap();
let min = v.iter().min().unwrap();
Rust 面向对象
Rust 面向对象编程
Rust 没有传统类继承,但通过 struct + trait 实现面向对象的核心能力。
1. 封装(Encapsulation)
// 字段私有,通过方法访问
pub struct BankAccount {
balance: f64, // 私有字段
}
impl BankAccount {
pub fn new(initial: f64) -> BankAccount {
BankAccount { balance: initial }
}
pub fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
pub fn withdraw(&mut self, amount: f64) -> bool {
if amount <= self.balance {
self.balance -= amount;
true
} else {
false
}
}
pub fn balance(&self) -> f64 {
self.balance
}
}
fn main() {
let mut acc = BankAccount::new(100.0);
acc.deposit(50.0);
acc.withdraw(30.0);
println!("余额: {}", acc.balance()); // 120
// acc.balance = 0; // 错误!私有
}
2. 多态(Trait 代替继承)
trait Shape {
fn area(&self) -> f64;
fn name(&self) -> &str;
}
struct Circle { radius: f64 }
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn name(&self) -> &str { "圆形" }
}
struct Rectangle { w: f64, h: f64 }
impl Shape for Rectangle {
fn area(&self) -> f64 { self.w * self.h }
fn name(&self) -> &str { "矩形" }
}
// 静态分发(泛型)
fn print_area(s: &T) {
println!("{}面积: {}", s.name(), s.area());
}
// 动态分发(trait 对象)
fn print_all(shapes: &[Box]) {
for s in shapes {
println!("{}面积: {}", s.name(), s.area());
}
}
fn main() {
let c = Circle { radius: 2.0 };
let r = Rectangle { w: 3.0, h: 4.0 };
print_area(&c); // 圆形面积: 12.566...
print_area(&r); // 矩形面积: 12
let shapes: Vec> = vec![Box::new(c), Box::new(r)];
print_all(&shapes);
}
3. 默认实现与继承模拟
// trait 默认实现 = 接口默认方法
trait Animal {
fn name(&self) -> &str;
fn speak(&self) -> String {
format!("{} 发出声音", self.name())
}
}
struct Dog;
impl Animal for Dog {
fn name(&self) -> &str { "狗" }
// 使用默认 speak
}
struct Cat;
impl Animal for Cat {
fn name(&self) -> &str { "猫" }
fn speak(&self) -> String {
format!("{} 喵喵叫", self.name()) // 覆盖
}
}
fn main() {
println!("{}", Dog.speak()); // 狗 发出声音
println!("{}", Cat.speak()); // 猫 喵喵叫
}
4. 组合优于继承
// Rust 风格: 用组合代替继承
struct Engine {
power: u32,
}
struct Car {
engine: Engine, // 组合
brand: String,
}
impl Car {
fn describe(&self) -> String {
format!("{} 汽车, {} 马力",
self.brand, self.engine.power)
}
}
5. 状态模式(State Pattern)
// 用类型系统表达状态,编译期防错
struct Draft;
struct Published;
struct Post {
content: String,
state: std::marker::PhantomData,
}
impl Post {
fn new() -> Post {
Post { content: String::new(), state: PhantomData }
}
fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
fn publish(self) -> Post {
Post { content: self.content, state: PhantomData }
}
}
impl Post {
fn content(&self) -> &str { &self.content }
}
fn main() {
let mut post = Post::new();
post.add_text("内容");
let post = post.publish();
println!("{}", post.content()); // 内容
// post.add_text("x"); // 错误!Published 无此方法
}
6. Rust OOP 最佳实践
1. 封装: 字段私有 + pub 方法
2. 多态: trait 约束(优先泛型静态分发)
3. 复用: 组合 + trait 默认实现
4. 状态: 用类型系统建模状态(编译期安全)
5. 避免: 过度依赖 Box 动态分发
6. 理念: "关注行为(trait)而非继承层次"
Rust 并发编程
Rust 并发编程
Rust 的并发安全由编译期保证,通过 Send/Sync trait 在编译期杜绝数据竞争。
1. 线程创建
use std::thread;
use std::time::Duration;
fn main() {
// 创建线程
let handle = thread::spawn(|| {
for i in 1..5 {
println!("子线程: {}", i);
thread::sleep(Duration::from_millis(100));
}
});
// 主线程
for i in 1..3 {
println!("主线程: {}", i);
thread::sleep(Duration::from_millis(50));
}
handle.join().unwrap(); // 等待子线程结束
}
2. move 捕获
use std::thread;
fn main() {
let v = vec![1, 2, 3];
// move: 所有权移入线程
let handle = thread::spawn(move || {
println!("{:?}", v); // v 在这里可用
});
// println!("{:?}", v); // 错误!v 已移动
handle.join().unwrap();
}
3. 共享状态(Mutex)
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Arc: 原子引用计数(多线程共享)
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 h in handles {
h.join().unwrap();
}
println!("结果: {}", *counter.lock().unwrap()); // 10
}
4. 消息传递(mpsc 通道)
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
// 通道: 发送者 + 接收者
let (tx, rx) = mpsc::channel();
// 生产者
thread::spawn(move || {
for i in 1..=5 {
tx.send(i).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
// 消费者(主线程)
for received in rx {
println!("收到: {}", received);
}
}
// 多生产者
fn multi_producer() {
let (tx, rx) = mpsc::channel();
for i in 0..3 {
let tx = tx.clone(); // 克隆发送者
thread::spawn(move || {
tx.send(format!("线程 {}", i)).unwrap();
});
}
drop(tx); // 关闭原始发送者
for msg in rx {
println!("{}", msg);
}
}
5. Send 与 Sync
// Send: 类型可安全移动到另一个线程
// Sync: 类型可安全被多个线程引用共享
// 大多数类型自动实现
// 原始类型、Vec、String 都 Send + Sync
// Rc(非原子引用计数)不是 Send(用 Arc)
// 自定义类型标注
// 编译期强制检查,违反即报错(安全!)
struct MyData {
value: i32,
}
// 若 MyData 含 Rc,则不能跨线程
6. 原子类型
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
counter.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { h.join().unwrap(); }
println!("{}", counter.load(Ordering::Relaxed)); // 10
}
// 原子类型: AtomicBool、AtomicUsize、AtomicI32 等
7. 并发最佳实践
1. 简单任务: thread::spawn + join
2. 共享数据: Arc + Mutex(或用原子类型)
3. 数据流: mpsc 通道(消息传递优先于共享状态)
4. 大规模并发: 用 Tokio 异步运行时
5. 线程池: rayon(数据并行)等 crate
6. 避免: 持有锁跨 await、死锁顺序不一致
Rust 宏
Rust 宏(Macro)
宏是编译期展开的代码生成,println!、vec! 等实际都是宏。
1. 声明宏(declarative macro)
// macro_rules! 定义声明宏
macro_rules! say_hello {
() => {
println!("Hello!");
};
}
fn main() {
say_hello!(); // Hello!
}
// 带参数
macro_rules! create_function {
($name:ident) => {
fn $name() {
println!("函数 {:?} 被调用", stringify!($name));
}
};
}
create_function!(foo);
create_function!(bar);
fn main() {
foo();
bar();
}
2. 匹配模式
macro_rules! my_vec {
// 空
() => {
Vec::new()
};
// 单个元素
($x:expr) => {
{
let mut v = Vec::new();
v.push($x);
v
}
};
// 多个元素
($($x:expr),*) => {
{
let mut v = Vec::new();
$(v.push($x);)*
v
}
};
}
fn main() {
let a = my_vec!(); // []
let b = my_vec!(1); // [1]
let c = my_vec!(1, 2, 3); // [1, 2, 3]
println!("{:?}", c);
}
3. 常用宏
// 标准库常用宏
println!("格式化输出");
vec![1, 2, 3]; // 创建 Vec
format!("{}-{}", 1, 2); // 格式化字符串
panic!("出错啦"); // 触发 panic
assert!(true); // 断言
assert_eq!(1, 1); // 断言相等
matches!(x, Some(_)); // 模式匹配判断
write!(w, "text"); // 写入 io
include_str!("file"); // 编译期包含文件内容
// 常用宏库
// serde_json::json!({...})
// lazy_static!
// todo!() / unimplemented!()
4. 过程宏(procedural macro)
// 过程宏: 用代码生成代码,更强大
// 三类: 派生宏、属性宏、函数宏
// 派生宏(derive)
#[derive(Debug, Clone, PartialEq)]
struct User { name: String }
// 自定义派生宏示例(serde)
#[derive(serde::Serialize, serde::Deserialize)]
struct Config {
host: String,
port: u16,
}
// 属性宏
#[tokio::main]
async fn main() {
println!("tokio 属性宏");
}
// 函数宏
let json = serde_json::json!({
"name": "张三",
"age": 28
});
5. 派生宏实现(示例)
// 用 derive_more 等 crate 简化
// 或自己写过程宏(proc-macro crate)
use proc_macro::TokenStream;
// 简单过程宏
#[proc_macro]
pub fn answer(input: TokenStream) -> TokenStream {
"42".parse().unwrap()
}
// 使用: my_macro::answer!() → 42
6. 宏 vs 函数
宏:
- 编译期执行,零运行时开销
- 可变参数个数
- 可生成代码
- 调试困难、编译变慢
函数:
- 运行时调用
- 参数数量固定
- 类型检查更强
- 易调试
原则: 优先用函数,需要元编程/变长参数再用宏
Rust 智能指针
Rust 智能指针
智能指针是带所有权与额外行为的指针,Box、Rc、RefCell 是最常用的三种。
1. Box(堆分配)
// Box: 在堆上分配,栈上只存指针
// 用于: 递归类型、大对象、trait 对象
fn main() {
let b = Box::new(5);
println!("{}", b); // 5 自动解引用
// 递归类型(需要间接引用)
enum List {
Cons(i32, Box),
Nil,
}
// trait 对象
trait Draw { fn draw(&self); }
struct Circle;
impl Draw for Circle {
fn draw(&self) { println!("圆"); }
}
let shape: Box = Box::new(Circle);
shape.draw();
}
2. Rc(引用计数)
// Rc: 单线程多所有权(引用计数)
// 多个变量共享同一数据
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // 引用计数 +1
let c = Rc::clone(&a); // 计数 +1
println!("计数: {}", Rc::strong_count(&a)); // 3
println!("{} {} {}", a, b, c);
}
// 场景: 图结构、共享配置、观察者模式
// 注意: Rc 不是 Send,不能跨线程
3. RefCell(内部可变性)
// RefCell: 运行时借用检查(编译期规则放宽)
// 场景: 需要在不可变引用下修改
use std::cell::RefCell;
struct MockMessenger {
messages: RefCell>,
}
impl MockMessenger {
fn send(&self, msg: String) {
// &self 下修改内部数据
self.messages.borrow_mut().push(msg);
}
fn count(&self) -> usize {
self.messages.borrow().len()
}
}
fn main() {
let m = MockMessenger {
messages: RefCell::new(vec![]),
};
m.send(String::from("hi"));
m.send(String::from("hello"));
println!("{}", m.count()); // 2
}
4. Rc + RefCell 组合
// 循环引用/共享可变数据
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
// 多个持有者 + 可变
let shared = Rc::new(RefCell::new(5));
let a = Rc::clone(&shared);
let b = Rc::clone(&shared);
*a.borrow_mut() += 10; // 修改
println!("{}", b.borrow()); // 15(共享可见)
}
5. Arc(线程安全 Rc)
// Arc: 原子引用计数,可跨线程
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for _ in 0..3 {
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
println!("{:?}", data);
}));
}
for h in handles { h.join().unwrap(); }
}
// Arc + Mutex = 线程安全共享可变数据
6. 其他智能指针
Cow: 写时复制(借用或拥有)
Cell: 值语义的内部可变性(Copy 类型)
MutexGuard / RwLockGuard: 锁守卫
String / Vec: 也是智能指针(拥有堆数据)
Pin: 固定地址(异步中使用)
Weak: 弱引用(防循环引用)
7. 选型指南
单所有权: Box
多所有权(单线程): Rc
多所有权(多线程): Arc
内部可变性(单线程): RefCell / Cell
内部可变性(多线程): Mutex / RwLock
组合: Rc>、Arc>(常用)
注意: 优先用所有权与借用,智能指针是"最后手段"
Rust 异步编程
Rust 异步编程
Rust 异步基于 async/await,配合 Tokio 运行时,适合 IO 密集型高并发服务。
1. async/await 基础
// async fn: 返回 Future,惰性执行
async fn hello() -> String {
String::from("Hello")
}
// 需要运行时执行
// Cargo.toml: tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() {
let result = hello().await; // 等待完成
println!("{}", result);
}
2. 并发执行
use tokio::time::{sleep, Duration};
async fn task(name: &str, secs: u64) {
println!("{} 开始", name);
sleep(Duration::from_secs(secs)).await;
println!("{} 完成", name);
}
#[tokio::main]
async fn main() {
// 顺序执行(共 3 秒)
task("A", 1).await;
task("B", 2).await;
// 并发执行(共 2 秒)
let (a, b) = tokio::join!(
task("C", 1),
task("D", 2),
);
// join handle 方式
let h1 = tokio::spawn(task("E", 1));
let h2 = tokio::spawn(task("F", 1));
h1.await.unwrap();
h2.await.unwrap();
}
3. 异步 HTTP 请求
// Cargo.toml
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
use reqwest;
#[tokio::main]
async fn main() -> Result<(), Box> {
// GET 请求
let resp = reqwest::get("https://httpbin.org/get")
.await?
.text()
.await?;
println!("{}", resp);
// JSON 请求
let json: serde_json::Value = reqwest::get("https://httpbin.org/json")
.await?
.json()
.await?;
println!("{:?}", json);
Ok(())
}
4. 异步 Web 服务
// Cargo.toml
// axum = "0.7"
// tokio = { version = "1", features = ["full"] }
use axum::{routing::get, Router, Json};
use serde_json::{json, Value};
async fn hello() -> &'static str {
"Hello, Axum!"
}
async fn api() -> Json {
Json(json!({ "status": "ok" }))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello))
.route("/api", get(api));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await.unwrap();
println!("监听 3000 端口");
axum::serve(listener, app).await.unwrap();
}
5. 异步数据竞争防护
// 共享状态: Arc>
use std::sync::{Arc, Mutex};
use axum::{routing::get, Router, extract::State};
async fn counter(State(state): State>>) -> String {
let mut count = state.lock().unwrap();
*count += 1;
format!("计数: {}", count)
}
#[tokio::main]
async fn main() {
let state = Arc::new(Mutex::new(0));
let app = Router::new()
.route("/count", get(counter))
.with_state(state);
// ...
}
6. 异步陷阱与最佳实践
1. 不要在 async 中调用阻塞函数(用 tokio::task::spawn_blocking)
2. 不要在 .await 时持有 std Mutex 锁
3. 用 #[tokio::main] 简化入口
4. 长任务用 tokio::spawn 隔离
5. 定时任务用 tokio::time
6. 测性能: 默认单线程调 multithread 特性
// 阻塞任务处理
#[tokio::main]
async fn main() {
let heavy = tokio::task::spawn_blocking(|| {
// CPU 密集/阻塞代码
std::thread::sleep(std::time::Duration::from_secs(1));
42
});
println!("{}", heavy.await.unwrap());
}
7. 异步 vs 多线程选择
异步(async + Tokio):
- IO 密集型(网络、文件、数据库)
- 高并发连接(数万)
- 资源占用低
多线程(thread + Arc):
- CPU 密集型计算
- 简单任务
- 无 tokio 依赖
通用: 服务器开发几乎都用 async + Tokio