Rust 编程教程
欢迎学习 Rust 编程语言!本教程将带你从零基础开始,逐步掌握 Rust 的核心知识和技能。
什么是 Rust?
Rust 是一门系统编程语言,由 Mozilla 研发,专注于安全性、并发性和性能。它具有以下特点:
- 内存安全:通过所有权系统在编译时保证内存安全,无需垃圾回收
- 零成本抽象:高级抽象不会带来运行时开销
- 并发安全:编译器在编译时检测数据竞争
- 高性能:性能媲美 C/C++
- 实用性强:丰富的标准库和活跃的社区
为什么学习 Rust?
- 内存安全无需 GC:Rust 独特的所有权系统让它在保证内存安全的同时不牺牲性能
- 并发编程友好:编译器帮助你避免并发错误
- 行业需求增长:系统编程、WebAssembly、嵌入式开发等领域需求旺盛
- 优秀的开发体验:友好的编译器错误提示、强大的包管理器 Cargo
- 活跃的社区:丰富的第三方库(crates)和完善的学习资源
Rust 的应用场景
系统编程
- 操作系统内核
- 设备驱动
- 嵌入式系统
Web 开发
- WebAssembly 应用
- 后端服务(使用 Actix、Rocket 等框架)
- Web 服务器
命令行工具
- 现代化的 CLI 工具(如 ripgrep、bat、exa)
- 构建工具
网络服务
- 高性能代理服务器
- 分布式系统
- 微服务
游戏开发
- 游戏引擎
- 游戏逻辑
区块链
- 智能合约
- 区块链客户端
Rust 核心概念
所有权(Ownership)
所有权是 Rust 最独特的特性,它让 Rust 在没有垃圾回收器的情况下保证内存安全。
fn main() {
let s1 = String::from("hello"); // s1 拥有字符串的所有权
let s2 = s1; // 所有权从 s1 转移到 s2
// println!("{}", s1); // 错误!s1 不再有效
println!("{}", s2); // 正确
}
解释:
- Rust 中每个值都有一个所有者(owner)
- 同一时间只能有一个所有者
- 所有者离开作用域时,值会被自动释放
借用和引用
使用引用可以访问数据而不获取所有权。
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // 借用 s
println!("字符串 '{}' 的长度是 {}", s, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s 是引用,离开作用域时不会释放数据
借用规则
fn main() {
let mut s = String::from("hello");
// 规则:要么有一个可变引用,要么有多个不可变引用
let r1 = &s; // 不可变引用
let r2 = &s; // 不可变引用
println!("{} {}", r1, r2);
// r1 和 r2 不再使用
let r3 = &mut s; // 可变引用(正确,因为 r1、r2 已不再使用)
r3.push_str(" world");
println!("{}", r3);
}
教程目录
基础阶段
进阶阶段
- 结构体和枚举 - 自定义数据类型
- 错误处理 - panic! 和 Result
- 泛型和 Trait - 泛型编程和共享行为
- 生命周期 - 引用的有效性验证
- 模块系统 - 代码组织和模块管理
- 测试 - 单元测试和集成测试
高级阶段
知识速查
- 速查表 - Rust 常用语法和概念速查
学习建议
- 理解所有权:这是 Rust 最核心的概念,花时间彻底理解它
- 阅读编译器提示:Rust 编译器提供非常友好的错误信息
- 多写代码:Rust 的学习曲线较陡,实践是最好的老师
- 善用文档:使用
cargo doc和官方文档 - 参与社区:Rust 社区非常友好,有问题可以积极提问
版本说明
本教程基于 Rust 1.85+ 和 Rust 2024 Edition 编写。
Rust 2024 Edition 完整变更列表
Rust 2024 Edition 是迄今为止最大的版本更新,以下是完整的变更列表:
语言变更:
| 变更项 | 说明 |
|---|---|
| 异步闭包 | 支持 async || {} 语法定义异步闭包,以及 AsyncFn、AsyncFnMut、AsyncFnOnce trait |
| RPIT 生命周期捕获规则 | 返回位置 impl Trait 现在默认捕获所有作用域内的泛型参数(包括生命周期),可使用 use<..> 显式指定 |
| Unsafe extern 块 | extern 块现在必须使用 unsafe 关键字:unsafe extern "C" { ... } |
| Unsafe 属性 | #[export_name]、#[link_section]、#[no_mangle] 必须使用 #[unsafe(...)] 包装 |
| 禁止 static mut 引用 | 对 static mut 的引用现在会产生编译错误,必须使用 addr_of! 或 addr_of_mut! |
| unsafe 函数内操作 | unsafe_op_in_unsafe_fn lint 默认警告,unsafe fn 内部的 unsafe 操作需要显式 unsafe {} 块 |
| if let 临时变量作用域 | if let 表达式中临时变量的作用域变更 |
| 尾表达式临时变量作用域 | 块的尾表达式中临时变量的作用域变更 |
| 模式匹配保留 | 禁止某些可能导致混淆的模式组合,为未来改进铺路 |
| Never 类型回退变更 | 改变 ! 类型的强制转换行为 |
| 宏片段说明符 | expr 片段现在也匹配 const 和 _ 表达式 |
| 保留 gen 关键字 | 为未来的生成器块保留 gen 关键字 |
| 保留语法 | 保留 #"foo"# 风格字符串和 ## 标记 |
标准库变更:
| 变更项 | 说明 |
|---|---|
| Prelude 扩展 | Future 和 IntoFuture 自动导入到 prelude |
Box<[T]> IntoIterator | Box<[T]> 现在实现 IntoIterator |
| unsafe 函数 | std::env::set_var、std::env::remove_var 现在是 unsafe 函数 |
| 元组 trait | 元组支持 FromIterator 和 Extend(最多 12 个元素) |
RPIT 生命周期捕获规则详解
RPIT(Return Position Impl Trait,返回位置 impl Trait)是 Rust 中一个重要的特性,它允许函数返回一个抽象类型。Rust 2024 Edition 对生命周期捕获规则进行了重要变更。
什么是生命周期捕获?
当一个函数返回 impl Trait 时,编译器需要知道这个 opaque type(不透明类型)可以使用哪些泛型参数。"捕获"一个泛型参数意味着这个参数可以在隐藏类型(hidden type)中使用。
Rust 2024 的变更
在 Rust 2024 之前,返回位置的 impl Trait 只捕获:
- 所有类型参数和常量参数
- 出现在
impl Trait约束中的生命周期参数
从 Rust 2024 开始,默认捕获所有作用域内的泛型参数,包括生命周期参数:
// Rust 2021:不捕获 'a(因为 'a 没有出现在约束中)
fn f_2021<'a>(x: &'a ()) -> impl Sized {
// 等价于 impl Sized + use<>
*x // 错误!'a 未被捕获
}
// Rust 2024:自动捕获 'a
fn f_2024<'a>(x: &'a ()) -> impl Sized {
// 等价于 impl Sized + use<'a>
*x // 正确!'a 被自动捕获
}
使用 use<..> 显式指定捕获
可以使用 use<..> 语法显式控制捕获哪些参数:
// 显式捕获 'a 和 T
fn capture<'a, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> {
(x, y) // 可以使用 'a 和 T
}
// 不捕获任何参数
fn no_capture<'a, T>(x: &'a (), y: T) -> impl Sized + use<> {
42 // 返回固定值,不使用任何泛型参数
}
// 只捕获 T,不捕获 'a
fn capture_t_only<'a, T>(x: &'a (), y: T) -> impl Sized + use<T> {
y // 只使用 T
}
为什么要这个变更?
这个变更解决了之前开发者需要使用"技巧"来捕获生命周期的问题:
旧的"Captures 技巧"(Rust 2024 之前):
// 定义一个辅助 trait
trait Captures<T: ?Sized> {}
impl<T: ?Sized, U: ?Sized> Captures<T> for U {}
// 使用技巧捕获生命周期
fn old_style<'a, T>(x: &'a (), y: T) -> impl Sized + Captures<(&'a (), T)> {
(x, y)
}
Rust 2024+ 的简洁写法:
fn new_style<'a, T>(x: &'a (), y: T) -> impl Sized {
(x, y) // 自动捕获 'a 和 T
}
迁移指南
运行 cargo fix --edition 可以自动处理大多数情况。如果需要保持旧行为,编译器会自动添加 use<> 约束:
// 迁移前(Rust 2021)
fn f<'a>(x: &'a ()) -> impl Sized { 42 }
// 自动迁移后(Rust 2024)
fn f<'a>(x: &'a ()) -> impl Sized + use<> { 42 }
迁移方式:
cargo fix --edition
详细迁移指南请参考 Rust Edition Guide。
参考资源
准备好开始学习了吗?点击下一章开始你的 Rust 编程之旅!