Rust编程代码规范指南
# Rust编程代码规范指南
本规范参考 Rust Style Guide (opens new window)、Rust API Guidelines (opens new window) 及 The Rust Book (opens new window),使用
rustfmt+clippy自动检查。
# 目录
- 01.规范概述
- 02.命名规范
- 03.代码格式规范
- 04.注释规范
- 05.所有权与借用
- 06.类型系统设计
- 07.错误处理
- 08.函数规范
- 09.模式匹配
- 10.模块组织
- 11.并发与异步
- 12.常用宏与derive
- 13.工具链与自动化
- 14.测试规范
- 15.常见反模式
- 16.代码审查清单
- 17.常见陷阱速查
# 01.规范概述
# 1.1 为何需要代码规范
疑惑:Rust 编译器已经非常严格,rustfmt 和 clippy 也能自动格式化,为什么还要制定规范?
答疑:编译器保证"正确",但不保证"优雅"。同一段逻辑可以用 match 也可以用 if let,可以用 unwrap() 也可以用 ?,可以 .clone() 到处用也可以精心设计所有权。规范的本质是用 Rust 社区公认的最佳实践写代码,让编译器、linter 和 Code Review 三者合力保证代码质量。
# 1.2 核心目标
| 目标 | 说明 |
|---|---|
| 可读性 | 代码一眼能看懂数据流和所有权转移 |
| 一致性 | 整个项目像一个人写的 |
| 安全性 | 从类型层面避免 panic、数据竞争、unsafe 滥用 |
| 性能 | 默认零开销抽象,避免不必要的 .clone() |
| 可审查性 | Code Review 聚焦逻辑而非格式 |
# 1.3 要求等级
- 必须(Mandatory):必须采用,违反将在 Code Review 中被驳回。部分规则由
clippy的deny级别强制执行。 - 推荐(Preferable):理应采用,特殊情况可不采用但需注释说明。
- 可选(Optional):团队自行决定。
# 1.4 Rust版本
代码应针对 Rust 2021 edition,使用 stable 工具链。nightly 特性需团队评审后引入。
# 02.命名规范
# 2.1 命名总表
| 类型 | 规则 | 示例 |
|---|---|---|
| 模块/文件名 | snake_case | user_service, db_connection |
| 类型/结构体/枚举/trait | CamelCase(大驼峰) | HttpClient, UserRepository, Iterator |
| 函数/方法 | snake_case | get_user(), parse_config |
| 常量/静态变量 | SCREAMING_SNAKE | MAX_RETRIES, DEFAULT_PORT |
| 局部变量 | snake_case | user_count, file_path |
| trait方法 | snake_case | fn parse(s: &str) -> Self |
| 泛型参数 | 单大写字母/大驼峰 | T, Item, Key, Value |
| 生命周期 | 短小写字母:'a, 'b, 'ctx | 'a, 'de, 'life |
| 构造器 | new() 为主,with_/from_ 为辅助 | User::new(), Config::from_env() |
| Cargo crate | snake_case(用 - 分隔词) | serde, tokio-util |
# 2.2 正确与错误示例
// ✅ 正确
pub struct UserService {
client: HttpClient,
cache: LruCache,
}
impl UserService {
pub fn new(client: HttpClient) -> Self { Self { client, cache: LruCache::new() } }
pub fn get_user(&self, user_id: i64) -> Option<&User> { self.cache.get(&user_id) }
}
const MAX_RETRY_COUNT: u32 = 3;
static DEFAULT_CONFIG: Lazy<Config> = Lazy::new(Config::default);
// ✅ 转换方法命名约定
// as_ → 廉价的引用转换(不转移所有权)
// to_ → 昂贵的值转换
// into_ → 消耗所有权的转换
// ❌ 错误
pub struct userservice {} // 类型未用 CamelCase
fn GetUser(user_id: i64) {} // 函数未用 snake_case
const maxRetry: u32 = 3; // 常量未用 SCREAMING_SNAKE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 2.3 命名反模式
| 反模式 | 示例 | 改进 |
|---|---|---|
| 过度缩写 | usr_svc, cfg | user_service, config |
| 含义不清 | data, info, tmp | user_data, config_info |
| 否定命名 | is_not_empty() | is_empty() 后取反 |
| 拼音命名 | dian_hua | phone_number |
| 冗余前缀 | MyUser, ImplService | User, Service |
| get_前缀滥用 | get_name() — 简单的字段访问 | name()(Rust 惯例:get 暗示有副作用) |
# 03.代码格式规范
# 3.1 缩进与空格 【必须】
// ✅ 4 个空格缩进(rustfmt 默认)
fn main() {
for i in 0..10 {
if i % 2 == 0 {
println!("{i}");
}
}
}
// ✅ 运算符两侧加空格
let result = a + b * c;
let ok = (x > 0) && (y < 10);
// ✅ 逗号后加空格、冒号后加空格
fn greet(name: &str, age: u32) -> String
let map: HashMap<String, i32> = HashMap::new();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3.2 大括号与换行 【必须】
// ✅ 左大括号不换行(同函数、结构体、impl 块、控制流)
struct User {
id: i64,
name: String,
}
impl User {
fn new(id: i64, name: String) -> Self {
Self { id, name }
}
}
// ✅ 参数过多时换行
fn send_notification(
user_id: i64,
title: &str,
content: &str,
notification_type: NotificationType,
) -> Result<(), Error> {
// ...
}
// ✅ 链式调用:. 开头换行
let active_names: Vec<&str> = users
.iter()
.filter(|u| u.is_active)
.map(|u| u.name.as_str())
.collect();
// ✅ 长条件换行
if user.is_active()
&& user.has_permission(Permission::Write)
&& !user.is_rate_limited()
{
proceed();
}
// 单行长度建议不超过 100 字符(rustfmt 默认)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 3.3 空行与 use 顺序
// ✅ use 分组顺序(rustfmt 默认):
// 1. std 库
// 2. 第三方 crate
// 3. 本地 crate(crate:: / self:: / super::)
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use crate::db::Connection;
use crate::models::User;
// ✅ 不同逻辑组之间空一行
struct UserService {
db: Connection,
cache: Mutex<LruCache>,
}
impl UserService {
// --- 构造 ---
pub fn new(db: Connection) -> Self {
Self { db, cache: Mutex::new(LruCache::new(100)) }
}
// --- 查询 ---
pub async fn get_user(&self, id: i64) -> Result<Option<User>> {
// ...
todo!()
}
// --- 写入 ---
pub async fn create_user(&self, user: NewUser) -> Result<User> {
// ...
todo!()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 04.注释规范
# 4.1 核心原则
注释解释"为什么",代码说明"做了什么"。
// ❌ 差的注释:复述代码
count += 1; // count 加 1
// ✅ 好的注释:解释意图
count += 1; // 跳过 CSV 文件的标题行
// ✅ 记录决策原因
// 使用 O(n²) 双重循环而非哈希表,因为 n ≤ 20,哈希开销更大
fn find_duplicates(items: &[Item]) -> Vec<&Item>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4.2 文档注释
// ✅ /// 文档注释(用于函数、结构体、模块的公开 API)
// ✅ //! 模块级文档注释(用于 mod.rs 或 lib.rs 的顶部)
//! # My Library
//!
//! 提供用户管理和认证功能。
//!
//! ## 使用示例
//! ```
//! use mylib::UserService;
//!
//! let service = UserService::new(client);
//! let user = service.get_user(123)?;
//! ```
/// 用户管理服务,负责用户的 CRUD 和权限管理。
///
/// # 线程安全性
///
/// 使用 [`tokio::sync::Mutex`] 保护内部状态,所有方法线程安全。
///
/// # 示例
///
/// ```
/// let service = UserService::new(conn);
/// let user = service.get_user(123).await?;
/// ```
pub struct UserService {
db: Connection,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 4.3 函数注释模板
/// 根据 ID 查询用户信息。
///
/// 优先从缓存获取,未命中时查数据库并回填缓存。
///
/// # 参数
/// - `user_id` - 用户唯一标识,必须 > 0
///
/// # 返回
/// - `Ok(Some(User))` - 用户存在
/// - `Ok(None)` - 用户不存在
/// - `Err(AppError)` - 数据库或缓存错误
///
/// # Panics
/// 当 `user_id <= 0` 时 panic(debug 模式下通过 `debug_assert!` 检查)
pub async fn get_user(&self, user_id: i64) -> Result<Option<User>, AppError>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4.4 TODO 与 FIXME
// TODO(yc): 添加分页查询支持,预计 v2.0 实现
fn get_all_users(&self) -> Vec<User>
// FIXME(yc): user_id 为 i64::MAX 时缓存 key 溢出,需要改用复合 key
fn cache_user(&self, user_id: i64, user: &User)
// HACK(yc): 第三方 crate 的 bug,等待上游修复后移除(#1234)
fn workaround_for_sdk_bug(&self)
// SAFETY: 手动管理裸指针,因为 FFI 接口要求(#4567)
unsafe fn ffi_wrapper(ptr: *const u8, len: usize)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 05.所有权与借用
# 5.1 借用优先原则 【必须】
// ✅ 优先借用 &T / &mut T(不影响调用方所有权)
fn process(data: &[u8]) -> usize {
data.len() // 只读借用
}
fn update(config: &mut Config) {
config.timeout = 30; // 可变借用
}
// ✅ 需要所有权时才获取所有权
fn consume(data: Vec<u8>) {
// data 在此函数结束时被 drop
}
// ✅ 借用规则(编译器强制执行):
// 1. 同时只能有一个可变借用,或多个不可变借用
// 2. 借用不能超过所有者的生命周期
// 3. 不可变借用期间,所有者也不能被移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 5.2 所有权转移时机 【推荐】
// ✅ 明确所有权转移:参数按值接收
fn save_to_db(user: User) -> Result<(), Error> {
db.insert(user)?; // user 的所有权转移到函数
Ok(())
}
// ✅ 调用方需要保留所有权时传引用
fn validate(user: &User) -> bool {
user.name.len() >= 2
}
// ✅ 返回值转移所有权
fn create_user(name: String) -> User {
User { id: next_id(), name } // name 所有权移入 User
}
// ✅ Option take:取出所有权,留下 None
fn take_job(&mut self) -> Option<Job> {
self.current_job.take() // 取出所有权,不破坏结构体
}
// ✅ mem::replace / mem::swap:原地替换所有权
fn update_config(&mut self, new: Config) -> Config {
std::mem::replace(&mut self.config, new)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 5.3 Clone 的使用边界 【必须】
// ❌ 避免不必要的 clone() —— 它是代码审查中的红色警报
fn bad_example(data: &[u8]) -> Vec<u8> {
let copy = data.to_vec(); // 克隆了整个切片
// ... 只是读操作
copy
}
// ✅ 只需要只读访问时用借用
fn good_example(data: &[u8]) -> usize {
data.len() // 零拷贝
}
// ✅ 需要修改时,让调用方决定是否克隆
fn process_and_modify(data: &mut Vec<u8>) {
data.push(0); // 就地修改
}
// ✅ clone() 的合理场景:
// 1. 确实需要独立副本且无法用借用
// 2. 闭包需要 move 所有权但外部仍需使用
// 3. 性能分析证明 clone 不是瓶颈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 5.4 生命周期标注 【推荐】
// ✅ 简单场景:编译器自动推断,无需标注
fn first_word(s: &str) -> &str { // 生命周期省略规则自动推导
s.split_whitespace().next().unwrap_or("")
}
// ✅ 需要标注的场景:多个引用参数,返回值与某个参数关联
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// ✅ 结构体持有引用时标注生命周期
struct Excerpt<'a> {
part: &'a str,
}
// ✅ 生命周期约束('b: 'a 表示 'b 活得比 'a 长)
fn foo<'a, 'b: 'a>(x: &'a str, y: &'b str) -> &'a str
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 5.5 智能指针选型 【推荐】
// Box<T> → 堆分配、递归类型、trait 对象
// Rc<T> → 单线程引用计数(不可变共享)
// Arc<T> → 多线程引用计数
// Cow<'a, T> → 写时复制(读用借用,写才分配)
// RefCell<T> → 运行时借用检查(单线程内部可变性)
// Mutex<T> → 多线程互斥锁
// ✅ Box:递归类型
enum List {
Cons(i32, Box<List>), // 需要 Box 打破无限大小
Nil,
}
// ✅ Arc + Mutex:多线程共享可变状态
let counter = Arc::new(Mutex::new(0));
// clone Arc 给其他线程...
// ✅ Cow:延迟克隆
fn process(input: &str) -> Cow<'_, str> {
if input.contains(' ') {
Cow::Owned(input.replace(' ', "_")) // 需要修改时才分配
} else {
Cow::Borrowed(input) // 不需要修改,零拷贝
}
}
// ❌ 不要为了"绕过编译器"而使用 RefCell —— 重新审视所有权设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 06.类型系统设计
# 6.1 struct 设计 【推荐】
// ✅ 字段按访问频率排序(热数据在前)
pub struct User {
pub id: i64, // 常用
pub name: String, // 常用
pub email: Option<String>, // 较常用
pub metadata: Metadata, // 较少访问
}
// ✅ 数据字段全部 private,通过方法访问(封装)
pub struct Config {
timeout: Duration,
retries: u32,
}
impl Config {
pub fn timeout(&self) -> Duration { self.timeout }
pub fn retries(&self) -> u32 { self.retries }
pub fn set_timeout(&mut self, t: Duration) { self.timeout = t }
}
// ✅ Builder 模式:复杂构造
pub struct HttpClientBuilder {
timeout: Option<Duration>,
retries: Option<u32>,
proxy: Option<String>,
}
impl HttpClientBuilder {
pub fn timeout(mut self, t: Duration) -> Self { self.timeout = Some(t); self }
pub fn build(self) -> HttpClient { /* ... */ }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 6.2 枚举设计 【推荐】
// ✅ 错误类型用枚举
#[derive(Debug)]
pub enum AppError {
Io(std::io::Error),
Config(String),
NotFound { entity: String, id: i64 },
}
// ✅ 状态机用枚举
enum ConnectionState {
Disconnected,
Connecting { attempts: u32 },
Connected { session_id: String },
Failed { error: String, retry_after: Duration },
}
// ✅ 带数据的枚举替代多个 Option 字段
// ❌ struct Request { data: Option<Json>, error: Option<String> } -- 可能同时为 None/Some
// ✅ enum Response { Success(Json), Error(String) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 6.3 trait 设计 【推荐】
// ✅ trait 小而精:一个 trait 做一件事
pub trait Repository {
type Item;
type Error;
async fn find(&self, id: i64) -> Result<Option<Self::Item>, Self::Error>;
async fn save(&self, item: Self::Item) -> Result<(), Self::Error>;
}
// ✅ 为特定类型实现 trait(孤儿规则允许)
impl Repository for PostgresRepo {
type Item = User;
type Error = AppError;
// ...
}
// ✅ trait 对象:动态分发(运行时多态)
fn process(repo: &dyn Repository<Item = User, Error = AppError>) { }
// ✅ 泛型 + trait bound:静态分发(编译期单态化)
fn process<R: Repository<Item = User>>(repo: &R) { }
// ✅ 组合优于继承 → Rust 没有继承,trait 就是组合方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 6.4 泛型与约束 【推荐】
// ✅ 用 where 子句增强可读性
fn complex<T, U>(t: T, u: U) -> U
where
T: Display + Clone + Send + Sync + 'static,
U: Default + From<T>,
{
// ...
}
// ✅ impl Trait:简化泛型参数(返回值/参数位置)
fn make_iter() -> impl Iterator<Item = i32> {
(0..10).filter(|x| x % 2 == 0)
}
// ❌ 不要用裸泛型参数不加约束 —— 函数体内能做的事太少
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 6.5 newtype 模式 【可选】
// ✅ 类型安全的包装(零开销)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UserId(i64); // 不会与 OrderId(i64) 混淆
#[derive(Debug, Clone)]
pub struct Password(String);
impl Password {
pub fn new(s: String) -> Result<Self, PasswordError> {
if s.len() >= 8 { Ok(Self(s)) }
else { Err(PasswordError::TooShort) }
}
pub fn as_str(&self) -> &str { &self.0 }
}
// ✅ 绕开孤儿规则:为外部类型实现外部 trait(需 newtype)
struct MyVec<T>(Vec<T>);
impl<T> Display for MyVec<T> where T: Display { /* ... */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 07.错误处理
# 7.1 Result vs panic 【必须】
// ✅ 可恢复的错误用 Result
fn parse(s: &str) -> Result<i32, ParseIntError> {
s.parse() // 不 panic
}
// ✅ 不可恢复的错误 / 编程错误用 panic(但优先 Result)
fn divide(a: i32, b: i32) -> i32 {
assert_ne!(b, 0, "division by zero"); // 编程错误,panic 是合理的
a / b
}
// ✅ 对外 API 永远不要 panic → 返回 Result
// ✅ 内部可用的 panic 场景:
// 1. 违反函数前置条件(调用方 bug)
// 2. 不可恢复的系统错误
// 3. 测试代码
// ❌ 不要用 panic 做控制流 → 用 Result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 7.2 ? 操作符传播 【必须】
// ✅ ? 操作符:快速传播错误
fn read_config(path: &str) -> Result<Config, AppError> {
let content = std::fs::read_to_string(path)?; // io::Error → AppError
let config: Config = toml::from_str(&content)?; // toml::Error → AppError
Ok(config)
}
// ✅ ? 也可用于 Option
fn get_or_default(map: &HashMap<i64, User>, id: i64) -> User {
let user = map.get(&id)?; // Option → 提前返回
Some(user.clone())
}
// ✅ try 块(nightly)或闭包内使用 ?
fn batch_process(items: &[String]) -> Result<Vec<Data>, Error> {
items.iter().map(|s| parse(s)).collect::<Result<Vec<_>, _>>()
// ^^^^^^^^^? 在闭包内传播
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 7.3 自定义错误类型 【推荐】
// ✅ thiserror:derive Error(推荐用于库)
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error), // #[from] 自动实现 From + ?
#[error("Config parse error: {0}")]
Config(String),
#[error("{entity} not found: id={id}")]
NotFound { entity: String, id: i64 },
#[error("Internal error")]
Internal(#[source] Box<dyn std::error::Error + Send + Sync>),
}
// ✅ anyhow:快速原型/应用层(不需要枚举错误类型)
use anyhow::{Context, Result};
fn read_file(path: &str) -> anyhow::Result<String> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read {path}"))?;
Ok(content)
}
// ✅ 选择:
// thiserror → 库(让调用方能精确匹配错误)
// anyhow → 应用/二进制(不在乎具体类型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 7.4 Option 处理 【必须】
// ✅ Option → Result 转换
let user = find_user(id).ok_or(AppError::NotFound {
entity: "User".into(),
id,
})?;
// ✅ Option 链式处理
let name = find_user(id)
.map(|u| u.name)
.unwrap_or_else(|| "Unknown".to_string());
// ✅ if let 单分支
if let Some(user) = find_user(id) {
println!("{}", user.name);
}
// ❌ 避免裸 unwrap() —— 除非你能证明不可能为 None
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 7.5 错误处理分层
┌─────────────────────────────────────────────────┐
│ 层 │ 策略 │
├────────────────┼─────────────────────────────────┤
│ 对外 API │ Result<T, AppError> │
│ 内部函数 │ Result<T, AppError> + ? │
│ 不可能路径 │ unreachable!() / debug_assert! │
│ 违反前置条件 │ assert! / panic!(调用方 bug) │
│ 测试 │ unwrap() / expect() │
└─────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 08.函数规范
# 8.1 短函数与卫语句 【推荐】
// ✅ 函数 ≤ 40 行,做一件事
// ✅ 卫语句减少嵌套
// ❌ 嵌套太深
fn process(order: &Order) -> Result<(), Error> {
if order.is_valid() {
if !order.is_processed() {
if let Some(payment) = &order.payment {
process_payment(payment)?;
}
}
}
Ok(())
}
// ✅ 卫语句扁平化
fn process(order: &Order) -> Result<(), Error> {
if !order.is_valid() { return Ok(()); }
if order.is_processed() { return Ok(()); }
let payment = order.payment.as_ref()
.ok_or(Error::MissingPayment)?;
process_payment(payment)
}
// ✅ let-else 卫语句(Rust 1.65+)
fn process(config: Option<Config>) -> Result<(), Error> {
let Some(config) = config else {
return Err(Error::MissingConfig);
};
// config 在这里是 Config(非 Option)
do_work(&config)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 8.2 泛型函数 【推荐】
// ✅ 泛型 + trait bound:零开销抽象
fn print_all<T: Display>(items: &[T]) {
for item in items {
println!("{item}");
}
}
// ✅ impl Trait 参数:简化签名
fn print_all(items: &[impl Display]) { /* ... */ }
// ✅ 返回 impl Trait(不暴露具体类型)
fn parse_lines(s: &str) -> impl Iterator<Item = &str> + '_ {
s.lines().filter(|l| !l.is_empty())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 8.3 闭包规范 【推荐】
// ✅ 短闭包(≤5 行)用闭包,长的提取为函数
let evens: Vec<_> = (0..100).filter(|x| x % 2 == 0).collect();
// ✅ move 闭包:转移所有权
let name = String::from("Alice");
let greet = move || println!("Hello, {name}!"); // name 所有权移入闭包
// ✅ 闭包类型标注(编译器无法推导时)
let f: Box<dyn Fn(i32) -> i32> = if condition {
Box::new(|x| x + 1)
} else {
Box::new(|x| x * 2)
};
// ❌ 闭包内嵌套闭包 → 提取为函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 8.4 迭代器优先 【推荐】
// ✅ 迭代器:声明式、零开销(编译器优化后等价于手写循环)
let active_names: Vec<&str> = users
.iter()
.filter(|u| u.is_active)
.map(|u| u.name.as_str())
.collect();
// ❌ 传统 for 循环 + 手动 push(冗长)
// let mut active_names = Vec::new();
// for user in &users {
// if user.is_active {
// active_names.push(user.name.as_str());
// }
// }
// ✅ 及早返回:first/find/any/all
let has_admin = users.iter().any(|u| u.role == Role::Admin);
let first_active = users.iter().find(|u| u.is_active);
// ✅ fold / reduce 累积
let total: u64 = orders.iter().map(|o| o.amount).sum();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 09.模式匹配
# 9.1 match 穷举 【必须】
// ✅ match 穷举所有分支(编译器强制)
let status = match code {
200..=299 => Status::Success,
301 | 302 => Status::Redirect, // | 匹配多个值
400..=499 => Status::ClientError,
500..=599 => Status::ServerError,
_ => Status::Unknown, // 通配(非穷举时需要)
};
// ✅ match 守卫(match guard)
let category = match score {
n if n >= 90 => "A",
n if n >= 80 => "B",
n if n >= 60 => "C",
_ => "D",
};
// ✅ @ 绑定:匹配 + 绑定变量
match msg {
Msg::Custom { id: id_val @ 1..=99 } => {
println!("Low priority: {id_val}");
}
Msg::Custom { id: id_val @ 100.. } => {
println!("High priority: {id_val}");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 9.2 if let 与 let-else 【推荐】
// ✅ if let:只关心一个分支
if let Some(user) = find_user(id) {
println!("Name: {}", user.name);
}
// ✅ if let + else
if let Some(config) = load_config() {
start_service(config);
} else {
eprintln!("No config, using defaults");
start_service(Config::default());
}
// ✅ let-else:提前返回/panic(Rust 1.65+)
let Some(config) = load_config() else {
return Err(AppError::MissingConfig);
};
// config 已经是 Config,不是 Option<Config>
// ✅ while let:循环消费迭代器
while let Some(item) = iter.next() {
process(item);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 9.3 matches! 宏 【推荐】
// ✅ matches! 宏:布尔表达式快速匹配
let is_success = matches!(result, Ok(_));
let is_not_found = matches!(err, AppError::NotFound { .. });
// ✅ 在断言中使用
assert!(matches!(result, Err(AppError::Timeout)));
// ✅ 避免冗长的 if let ... true else false
// ❌ let is_ok = if let Ok(_) = result { true } else { false };
// ✅ let is_ok = matches!(result, Ok(_));
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 10.模块组织
# 10.1 mod 声明与可见性 【必须】
// ✅ 模块默认私有,按需 pub
mod api; // 私有模块
mod db;
pub mod models; // 公开模块
pub mod service;
// ✅ 结构体/枚举默认私有字段
pub struct User {
pub id: i64, // 公开字段
name: String, // 私有字段
email: Option<String>, // 私有字段
}
// ✅ 函数/方法默认私有
impl User {
pub fn new(id: i64, name: String) -> Self { /* ... */ }
pub fn name(&self) -> &str { &self.name } // getter
fn validate(&self) -> bool { /* ... */ } // 私有
}
// ✅ pub(crate) / pub(super) 限定作用域
pub(crate) fn internal_helper() { } // 仅 crate 内可见
pub(super) fn parent_helper() { } // 仅父模块可见
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 10.2 pub use 重导出 【推荐】
// ✅ 重导出:对外隐藏内部模块结构
// lib.rs
mod api;
mod db;
pub mod models;
mod service;
pub use api::HttpClient; // 用户直接用 crate::HttpClient
pub use models::{User, Role};
pub use service::UserService;
// 用户视角:
// use mycrate::UserService; // 不需要知道它在 service 模块中
// use mycrate::models::User; // 也可以走完整路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 10.3 模块文件布局 【推荐】
src/
├── lib.rs # crate root
├── main.rs # binary entry
├── api/
│ ├── mod.rs # mod api;
│ ├── client.rs # HttpClient
│ └── types.rs # Request, Response
├── db/
│ ├── mod.rs
│ └── postgres.rs
├── models/
│ ├── mod.rs
│ ├── user.rs
│ └── order.rs
└── service/
├── mod.rs
├── user_service.rs
└── order_service.rs
// 或者 Rust 2018+ 风格(不需要 mod.rs):
src/
├── lib.rs
├── api.rs # api/mod.rs 的替代
├── api/
│ ├── client.rs
│ └── types.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 11.并发与异步
# 11.1 Send 与 Sync 【必须】
// ✅ Send:类型可以安全地在线程间转移所有权
// ✅ Sync:类型的共享引用可以安全地在线程间共享
// 大多数标准类型自动实现 Send + Sync
// 包含 Rc / RefCell / 裸指针的类型不是 Send/Sync
// ❌ 不要为了"编译通过"而 unsafe impl Send —— 那是数据竞争
// ✅ 用 Arc 替代 Rc(多线程共享)
// ✅ 检查 Send/Sync(编译期保证,无需手动标注)
fn assert_send<T: Send>(t: T) {}
assert_send(MyStruct::new()); // 如果 MyStruct 不是 Send,编译报错
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 11.2 共享状态:Arc/Mutex/RwLock 【推荐】
use std::sync::{Arc, Mutex, RwLock};
// ✅ Arc<Mutex<T>>:多线程读写
let counter = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..10).map(|_| {
let counter = Arc::clone(&counter);
std::thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
})
}).collect();
for h in handles { h.join().unwrap(); }
assert_eq!(*counter.lock().unwrap(), 10);
// ✅ Arc<RwLock<T>>:读多写少
let cache = Arc::new(RwLock::new(HashMap::new()));
// 读
let data = cache.read().unwrap().get(&key).cloned();
// 写
cache.write().unwrap().insert(key, value);
// ✅ lock 后尽快释放(持有锁期间不要阻塞/await)
// ❌ let guard = mutex.lock(); do_io().await; -- 死锁风险!
// ✅ Tokio 中用 tokio::sync::Mutex(支持异步)
use tokio::sync::Mutex as AsyncMutex;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 11.3 Channel 通信 【推荐】
use std::sync::mpsc; // 多生产者单消费者
use crossbeam_channel; // 多生产者多消费者
use tokio::sync::mpsc as async_mpsc; // 异步 channel
// ✅ std::mpsc: 简单场景
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
tx.send("message").unwrap();
});
let msg = rx.recv().unwrap();
// ✅ tokio::sync::mpsc: 异步场景
let (tx, mut rx) = tokio::sync::mpsc::channel(32); // 有界 channel
tokio::spawn(async move {
tx.send("async message").await.unwrap();
});
while let Some(msg) = rx.recv().await {
process(msg).await;
}
// ✅ Channel 优于共享内存:Don't communicate by sharing memory;
// share memory by communicating.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 11.4 async/await 与 tokio 【推荐】
// ✅ #[tokio::main] 或 #[tokio::test]
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let db = connect_db().await?;
serve(db).await
}
// ✅ 并发执行多个 future
async fn fetch_all(ids: &[i64]) -> Vec<User> {
let futures: Vec<_> = ids.iter()
.map(|id| fetch_user(*id))
.collect();
futures::future::join_all(futures).await // 并发
}
// ✅ tokio::spawn:启动新任务(类似线程)
let handle = tokio::spawn(async move {
heavy_computation().await
});
let result = handle.await?;
// ✅ tokio::select!:同时等待多个 future
tokio::select! {
result = fetch_from_cache(key) => { /* 缓存命中 */ }
result = fetch_from_db(key) => { /* 数据库返回 */ }
_ = tokio::time::sleep(Duration::from_secs(5)) => { /* 超时 */ }
}
// ❌ 不要在 async fn 中调用 std::thread::sleep —— 阻塞整个线程
// ✅ 用 tokio::time::sleep
// ❌ 不要在持有 Mutex 锁时 .await —— 死锁
// ✅ 先 drop 锁,再 .await
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 12.常用宏与 derive
# 12.1 derive 自动实现 【必须】
// ✅ 按需 derive
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: i64,
pub name: String,
}
// ✅ derive 常用列表
#[derive(
Debug, // {:?} 打印
Clone, // .clone()
PartialEq, Eq, // ==, !=
PartialOrd, Ord, // <, >, 排序
Hash, // HashMap key
Default, // User::default()
Serialize, // 序列化(serde)
Deserialize, // 反序列化(serde)
)]
pub struct FullFeatured { /* ... */ }
// ⚠ Copy 要谨慎:只有简单值类型才 derive Copy
// Copy 后,赋值/传参不会转移所有权,可能导致"无意复制"
#[derive(Debug, Clone, Copy)] // ✅ i32, bool, 小数组等
struct Point { x: f64, y: f64 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 12.2 常用宏速查
// ✅ 构造宏
let v = vec![1, 2, 3]; // Vec
let s = format!("Hello, {name}!"); // 字符串格式化
let m = hashmap! { "a" => 1, "b" => 2 }; // HashMap(maplit crate)
// ✅ 断言宏
assert_eq!(result, expected);
assert_ne!(a, b);
assert!(result.is_ok());
assert!(result.is_err(), "Should fail: {result:?}");
// ✅ 占位宏
todo!("还没实现"); // 编译通过,运行 panic
unimplemented!("待实现"); // 同上 + 语义更明确
unreachable!("逻辑上不可能到达");
// ✅ 日志宏
log::info!("User {id} logged in");
log::error!("Failed: {e}");
log::debug!("Cache hit: {key}");
// ✅ compile_error!:编译期错误
compile_error!("This feature requires 'foo' feature flag");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 12.3 属性宏 【推荐】
// ✅ #[derive(...)]:自动实现 trait(最常见)
// ✅ #[cfg(...)]:条件编译
#[cfg(feature = "tls")]
fn connect_tls() { }
#[cfg(target_os = "linux")]
fn linux_only() { }
// ✅ #[allow(...)] / #[deny(...)]:控制 lint
#[allow(dead_code)]
fn unused_function() { }
// ✅ #[must_use]:强制使用返回值
#[must_use = "You must handle the potential error"]
pub fn try_connect() -> Result<(), Error> { }
// 调用方不用 let _ = try_connect(); → 编译警告
// ✅ #[non_exhaustive]:防止外部穷举枚举(库 API)
#[non_exhaustive]
pub enum ErrorKind { Io, Config, Network }
// 外部 match 必须加 _ 分支
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 13.工具链与自动化
# 13.1 rustfmt 格式化
# rustfmt:Rust 官方格式化工具(无需配置即可使用)
rustfmt src/main.rs # 格式化单个文件
cargo fmt # 格式化整个项目
cargo fmt -- --check # 仅检查(CI 用)
# rustfmt.toml(可选配置)
max_width = 100 # 最大行宽,默认 100
tab_spaces = 4 # 缩进空格,默认 4
edition = "2021"
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 13.2 clippy 静态检查
# clippy:Rust 官方 lint 工具(非常强大)
cargo clippy # 运行检查
cargo clippy -- -D warnings # 警告视为错误(CI 用)
cargo clippy --fix # 自动修复部分问题
# 常用 clippy lint(deny 级别)
# clippy::unwrap_used # 禁止 unwrap
# clippy::expect_used # 限制 expect
# clippy::clone_on_copy # Copy 类型不需要 clone
# clippy::redundant_clone # 冗余 clone
# clippy::todo # todo! 宏
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
// 在 lib.rs / main.rs 中配置 clippy
#![deny(clippy::unwrap_used)]
#![deny(clippy::todo)]
#![warn(clippy::pedantic)] // 开启所有 pedantic lint(团队决定)
1
2
3
4
2
3
4
# 13.3 CI 集成
# GitHub Actions 示例
- name: Rust Code Quality
run: |
cargo fmt --all -- --check # 格式检查
cargo clippy -- -D warnings # lint 检查(警告作为错误)
cargo check # 快速编译检查
cargo test # 单元测试
cargo audit # 安全漏洞检查(需安装)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 13.4 pre-commit 钩子 【推荐】
# .pre-commit-config.yaml
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
- id: cargo-check
- id: clippy
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 安装
pip install pre-commit
pre-commit install # 每次 commit 前自动运行
pre-commit run --all-files # 手动全量检查
1
2
3
4
2
3
4
# 14.测试规范
# 14.1 单元测试
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_positive() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, -2), -3);
}
#[test]
fn test_parse_valid() {
let result = parse("42");
assert!(result.is_ok());
assert_eq!(result.unwrap(), 42);
}
#[test]
fn test_parse_invalid() {
let result = parse("abc");
assert!(result.is_err());
}
// ✅ 测试 panic
#[test]
#[should_panic(expected = "division by zero")]
fn test_divide_by_zero() {
divide(1, 0);
}
// ✅ 异步测试
#[tokio::test]
async fn test_async_operation() {
let data = fetch_data().await.unwrap();
assert!(!data.is_empty());
}
// ✅ 参数化测试(用 rstest crate 或手动)
#[test]
fn test_multiple_cases() {
for (input, expected) in [
("1+1", 2),
("2*3", 6),
] {
assert_eq!(evaluate(input).unwrap(), expected);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 14.2 集成测试
tests/
├── common/
│ └── mod.rs # 共享测试工具
├── api_test.rs # 每个文件是一个独立的测试 crate
└── db_test.rs
1
2
3
4
5
2
3
4
5
// tests/api_test.rs
mod common;
#[tokio::test]
async fn test_create_user() {
let app = common::setup_test_app().await;
let user = app.create_user("Alice").await.unwrap();
assert_eq!(user.name, "Alice");
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 14.3 文档测试
/// 计算两个数的和。
///
/// # 示例
///
/// ```
/// use mycrate::add;
///
/// assert_eq!(add(2, 3), 5);
/// ```
///
/// # Panics
///
/// 不会 panic。
pub fn add(a: i32, b: i32) -> i32 { a + b }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
cargo test --doc # 运行文档测试
1
# 15.常见反模式
| 反模式 | 问题 | 改进 |
|---|---|---|
过多 .clone() | 性能差、语义不清 | 借用、重新设计所有权、或 Cow |
裸 unwrap() | 运行时 panic | ? / match / expect("原因") |
unsafe 滥用 | 内存安全风险 | 优先安全 Rust |
| 过长的函数(>40 行) | 难以理解 | 按职责拆分 |
| 全局可变状态 | 并发问题、难以测试 | 注入依赖、Arc<Mutex<T>> |
为"绕过编译器"用 RefCell | 运行时 panic 取代编译时检查 | 重新审视所有权设计 |
用 Vec 当万能容器 | 语义不清、性能可能差 | 选择合适的集合:HashMap, BTreeMap, VecDeque |
忽略 Result 返回值 | 错误被静默丢弃 | let _ = 或显式处理 |
#[allow(dead_code)] 随意加 | 掩盖设计问题 | 删除无用代码或明确说明保留原因 |
# 16.代码审查清单
每次 Code Review 时,按以下清单逐项检查:
## 命名
- [ ] 类型 CamelCase,函数/变量 snake_case,常量 SCREAMING_SNAKE
- [ ] 无拼音、单字母、含义模糊的命名
- [ ] get_ 前缀仅在有副作用时使用
- [ ] 转换方法命名正确(as_/to_/into_)
## 所有权与借用
- [ ] 无不必要的 .clone()
- [ ] 借用/所有权转移语义正确
- [ ] 生命周期标注无多余或缺失
- [ ] 智能指针选型合理(Box/Rc/Arc/Mutex)
## 错误处理
- [ ] 用 Result 而非 panic
- [ ] 无裸 unwrap()(除非逻辑上不可能失败 + 注释说明)
- [ ] 用 ? 传播错误
- [ ] 自定义错误类型语义清晰
- [ ] 错误信息包含足够上下文
## 模式匹配
- [ ] match 穷举所有分支(或含 _ 通配)
- [ ] 单分支用 if let
- [ ] 需要提前返回用 let-else
## unsafe
- [ ] 无 unsafe 块(除非有充分理由)
- [ ] unsafe 块有 SAFETY 注释说明前提条件
## 类型与 trait
- [ ] struct 字段私有,通过方法访问
- [ ] trait 小而精
- [ ] 泛型约束合理
- [ ] derive 宏使用恰当(Copy 谨慎)
## 并发
- [ ] 多线程共享用 Arc 而非 Rc
- [ ] 锁持有时间短,锁内无 .await
- [ ] Channel 通信优先于共享内存
## async
- [ ] async fn 中用 tokio::time::sleep(非 std::thread::sleep)
- [ ] 无 .await 在持有锁时
- [ ] task 取消安全(用 tokio::select! 处理)
## 测试
- [ ] 核心逻辑有单元测试
- [ ] 覆盖正常和异常路径
- [ ] 文档测试对公开 API 有示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 17.常见陷阱速查
以下陷阱即使在有经验的开发者中也常见,值得定期回顾。
# 17.1 所有权与借用陷阱
| # | 陷阱 | 描述 | 正解 |
|---|---|---|---|
| 1 | 借用 + 修改冲突 | v.push(v.len()) — 不可变借用和可变借用同时存在 | let len = v.len(); v.push(len); |
| 2 | 迭代中修改 | for item in vec { vec.push(...) } | 先 collect 再修改,或用索引 |
| 3 | move 后访问 | let s = String::new(); let t = s; println!("{s}"); | clone 后 move,或传引用 |
| 4 | 临时值生命周期 | let r = &String::from("hi"); — 临时值立即 drop | let s = String::from("hi"); let r = &s; |
# 17.2 生命周期陷阱
| # | 陷阱 | 描述 | 正解 |
|---|---|---|---|
| 1 | 返回局部引用 | 函数返回局部变量的引用 | 返回拥有所有权的值,或传参 |
| 2 | 结构体自引用 | struct S { a: String, b: &str } 无法构造 | 用 ouroboros 或重新设计 |
| 3 | 闭包生命周期 | 闭包捕获引用后返回 → 借用冲突 | move 闭包,或 clone |
| 4 | 异步生命周期 | async fn 返回的 Future 持有引用 | 'static 约束或用 owned 值 |
# 17.3 错误处理陷阱
| # | 陷阱 | 描述 | 正解 |
|---|---|---|---|
| 1 | unwrap() 在生产代码中 | panic 导致进程退出 | ? / match / expect("原因") |
| 2 | 忽略 Result 返回值 | fs::write(path, data); 编译器警告 | let _ = 或 .ok() |
| 3 | anyhow + thiserror 混用 | 库中用 anyhow 暴露不了错误类型 | 库用 thiserror,应用用 anyhow |
| 4 | ? 在 Option 和 Result 混用 | 函数返回 Result 但内部用 Option? | 统一错误类型或用 .ok_or()? |
# 17.4 并发陷阱
| # | 陷阱 | 描述 | 正解 |
|---|---|---|---|
| 1 | Mutex 中毒 | 持有锁的线程 panic → 锁 "中毒" | lock().unwrap() 或 lock().unwrap_or_else(|e| e.into_inner()) |
| 2 | 锁内 .await | Mutex 持有期间 await → 死锁 | drop 锁后再 await,或用 tokio::sync::Mutex |
| 3 | Rc 跨线程 | Rc 不是 Send | 用 Arc |
| 4 | Channel 关闭 | send 返回 Err(接收端已 drop) | 区分 send 错误和处理优雅关闭 |
# 17.5 类型与 trait 陷阱
| # | 陷阱 | 描述 | 正解 |
|---|---|---|---|
| 1 | trait 对象大小 | dyn Trait 在栈上是 ?Sized 的 | 用 Box<dyn Trait> 或 &dyn Trait |
| 2 | 泛型代码膨胀 | 为 N 种类型单态化 → 二进制增大 | 用 trait 对象或提取非泛型公共部分 |
| 3 | Deref 滥用 | 给 newtype 实现 Deref → 失去类型安全 | 显式调用 .0 或提供命名方法 |
| 4 | Iterator::collect 类型推断 | let v = items.collect(); 编译器不知道目标类型 | 标注 let v: Vec<_> = items.collect(); |
本文档将随 Rust 语言演进持续更新,欢迎通过 GitHub issues (opens new window) 反馈问题和建议。
上次更新: 2026/06/17, 11:39:29