编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 质量保障

  • 产品思考

  • 软实力

  • 开发流程

  • Git应用

  • 技术模版

  • 技术规范

    • 技术规范
    • C++编程代码规范指南
    • Java编程代码规范指南
    • JavaScript编程规范指南
    • TypeScript编程规范指南
    • Python编程代码规范指南
    • Go编程代码规范指南
    • Kotlin编程代码规范指南
    • Swift编程代码规范指南
    • Rust编程代码规范指南
      • 目录
      • 01.规范概述
        • 1.1 为何需要代码规范
        • 1.2 核心目标
        • 1.3 要求等级
        • 1.4 Rust版本
      • 02.命名规范
        • 2.1 命名总表
        • 2.2 正确与错误示例
        • 2.3 命名反模式
      • 03.代码格式规范
        • 3.1 缩进与空格 【必须】
        • 3.2 大括号与换行 【必须】
        • 3.3 空行与 use 顺序
      • 04.注释规范
        • 4.1 核心原则
        • 4.2 文档注释
        • 4.3 函数注释模板
        • 4.4 TODO 与 FIXME
      • 05.所有权与借用
        • 5.1 借用优先原则 【必须】
        • 5.2 所有权转移时机 【推荐】
        • 5.3 Clone 的使用边界 【必须】
        • 5.4 生命周期标注 【推荐】
        • 5.5 智能指针选型 【推荐】
      • 06.类型系统设计
        • 6.1 struct 设计 【推荐】
        • 6.2 枚举设计 【推荐】
        • 6.3 trait 设计 【推荐】
        • 6.4 泛型与约束 【推荐】
        • 6.5 newtype 模式 【可选】
      • 07.错误处理
        • 7.1 Result vs panic 【必须】
        • 7.2 ? 操作符传播 【必须】
        • 7.3 自定义错误类型 【推荐】
        • 7.4 Option 处理 【必须】
        • 7.5 错误处理分层
      • 08.函数规范
        • 8.1 短函数与卫语句 【推荐】
        • 8.2 泛型函数 【推荐】
        • 8.3 闭包规范 【推荐】
        • 8.4 迭代器优先 【推荐】
      • 09.模式匹配
        • 9.1 match 穷举 【必须】
        • 9.2 if let 与 let-else 【推荐】
        • 9.3 matches! 宏 【推荐】
      • 10.模块组织
        • 10.1 mod 声明与可见性 【必须】
        • 10.2 pub use 重导出 【推荐】
        • 10.3 模块文件布局 【推荐】
      • 11.并发与异步
        • 11.1 Send 与 Sync 【必须】
        • 11.2 共享状态:Arc/Mutex/RwLock 【推荐】
        • 11.3 Channel 通信 【推荐】
        • 11.4 async/await 与 tokio 【推荐】
      • 12.常用宏与 derive
        • 12.1 derive 自动实现 【必须】
        • 12.2 常用宏速查
        • 12.3 属性宏 【推荐】
      • 13.工具链与自动化
        • 13.1 rustfmt 格式化
        • 13.2 clippy 静态检查
        • 13.3 CI 集成
        • 13.4 pre-commit 钩子 【推荐】
      • 14.测试规范
        • 14.1 单元测试
        • 14.2 集成测试
        • 14.3 文档测试
      • 15.常见反模式
      • 16.代码审查清单
      • 17.常见陷阱速查
        • 17.1 所有权与借用陷阱
        • 17.2 生命周期陷阱
        • 17.3 错误处理陷阱
        • 17.4 并发陷阱
        • 17.5 类型与 trait 陷阱
    • Shell编程代码规范指南
    • 项目代码提交规范
  • markdown

  • mermaid

  • license

  • 博客部署

  • 技术招聘

  • 测试经验

  • 技术
  • 技术规范
杨充
2021-06-20
目录

Rust编程代码规范指南

# Rust编程代码规范指南

本规范参考 Rust Style Guide (opens new window)、Rust API Guidelines (opens new window) 及 The Rust Book (opens new window),使用 rustfmt + clippy 自动检查。

# 目录

  • 01.规范概述
    • 1.1 为何需要代码规范
    • 1.2 核心目标
    • 1.3 要求等级
    • 1.4 Rust版本
  • 02.命名规范
    • 2.1 命名总表
    • 2.2 正确与错误示例
    • 2.3 命名反模式
  • 03.代码格式规范
    • 3.1 缩进与空格
    • 3.2 大括号与换行
    • 3.3 空行与use顺序
  • 04.注释规范
    • 4.1 核心原则
    • 4.2 文档注释
    • 4.3 函数注释模板
    • 4.4 TODO与FIXME
  • 05.所有权与借用
    • 5.1 借用优先原则
    • 5.2 所有权转移时机
    • 5.3 Clone的使用边界
    • 5.4 生命周期标注
    • 5.5 智能指针选型
  • 06.类型系统设计
    • 6.1 struct设计
    • 6.2 枚举设计
    • 6.3 trait设计
    • 6.4 泛型与约束
    • 6.5 newtype模式
  • 07.错误处理
    • 7.1 Result vs panic
    • 7.2 ?操作符传播
    • 7.3 自定义错误类型
    • 7.4 Option处理
    • 7.5 错误处理分层
  • 08.函数规范
    • 8.1 短函数与卫语句
    • 8.2 泛型函数
    • 8.3 闭包规范
    • 8.4 迭代器优先
  • 09.模式匹配
    • 9.1 match穷举
    • 9.2 if let与let-else
    • 9.3 matches!宏
  • 10.模块组织
    • 10.1 mod声明与可见性
    • 10.2 pub use重导出
    • 10.3 模块文件布局
  • 11.并发与异步
    • 11.1 Send与Sync
    • 11.2 共享状态:Arc/Mutex/RwLock
    • 11.3 Channel通信
    • 11.4 async/await与tokio
  • 12.常用宏与derive
    • 12.1 derive自动实现
    • 12.2 常用宏速查
    • 12.3 属性宏
  • 13.工具链与自动化
    • 13.1 rustfmt格式化
    • 13.2 clippy静态检查
    • 13.3 CI集成
    • 13.4 pre-commit钩子
  • 14.测试规范
    • 14.1 单元测试
    • 14.2 集成测试
    • 14.3 文档测试
  • 15.常见反模式
  • 16.代码审查清单
  • 17.常见陷阱速查
    • 17.1 所有权与借用陷阱
    • 17.2 生命周期陷阱
    • 17.3 错误处理陷阱
    • 17.4 并发陷阱
    • 17.5 类型与trait陷阱

# 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 命名反模式

反模式 示例 改进
过度缩写 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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
// 在 lib.rs / main.rs 中配置 clippy
#![deny(clippy::unwrap_used)]
#![deny(clippy::todo)]
#![warn(clippy::pedantic)]      // 开启所有 pedantic lint(团队决定)
1
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

# 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
# 安装
pip install pre-commit
pre-commit install                  # 每次 commit 前自动运行
pre-commit run --all-files          # 手动全量检查
1
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

# 14.2 集成测试

tests/
├── common/
│   └── mod.rs           # 共享测试工具
├── api_test.rs           # 每个文件是一个独立的测试 crate
└── db_test.rs
1
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

# 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
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

# 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) 反馈问题和建议。

#Rust#代码规范
上次更新: 2026/06/17, 11:39:29
Swift编程代码规范指南
Shell编程代码规范指南

← Swift编程代码规范指南 Shell编程代码规范指南→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式