rust-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust Best Practices
Rust最佳实践
Unified Rust guidelines covering coding style, ownership, error handling, async patterns, traits, testing, performance, linting, and documentation. Apply when writing or reviewing Rust code.
统一的Rust开发规范,覆盖编码风格、所有权、错误处理、异步模式、trait、测试、性能、lint检查、文档相关内容,在编写或审查Rust代码时遵循使用。
When to Apply
适用场景
- Writing new Rust code or designing APIs
- Reviewing or refactoring existing Rust code
- Implementing async systems with Tokio
- Designing error hierarchies with thiserror/anyhow
- Choosing between borrowing, cloning, or ownership transfer
- Setting up tests, benchmarks, or snapshot testing
- Configuring clippy lints and workspace settings
- Optimizing Rust code for performance
- 编写新的Rust代码或设计API
- 审查或重构现有Rust代码
- 使用Tokio实现异步系统
- 基于thiserror/anyhow设计错误层级结构
- 在借用、克隆或所有权转移之间做选择
- 搭建测试、基准测试或快照测试
- 配置clippy lint规则和工作区设置
- 对Rust代码进行性能优化
Reference Guide
参考指南
Load detailed guidance based on context. Read the relevant file when the topic arises:
| Topic | Reference | Load When |
|---|---|---|
| Coding Style | | Naming, imports, iterators, comments, string handling, macros |
| Error Handling | | Result, Option, ?, thiserror, anyhow, custom errors, async errors |
| Ownership & Pointers | | Lifetimes, borrowing, smart pointers, Pin, Cow, interior mutability |
| Traits & Generics | | Trait design, dispatch, GATs, sealed traits, type state pattern |
| Async & Concurrency | | Tokio, channels, streams, shutdown, runtime config, async traits |
| Sync Concurrency | | Atomics, Mutex, RwLock, lock ordering, Send/Sync, memory ordering |
| Testing | | Unit/integration/doc tests, snapshot, proptest, mockall, benchmarks, fuzz |
| Performance | | Profiling, flamegraph, cloning, stack vs heap, iterators, allocation |
| Clippy & Linting | | Clippy config, key lints, workspace setup, #[expect] vs #[allow] |
| Documentation | | Doc comments, rustdoc, doc lints, coverage checklist |
可根据上下文加载详细指引,遇到对应主题时查阅相关文件:
| 主题 | 参考文档 | 触发加载场景 |
|---|---|---|
| 编码风格 | | 命名、导入、迭代器、注释、字符串处理、宏 |
| 错误处理 | | Result、Option、?、thiserror、anyhow、自定义错误、异步错误 |
| 所有权与指针 | | 生命周期、借用、智能指针、Pin、Cow、内部可变性 |
| Trait与泛型 | | Trait设计、分发、GAT、密封trait、类型状态模式 |
| 异步与并发 | | Tokio、通道、流、优雅关闭、运行时配置、异步trait |
| 同步并发 | | 原子量、Mutex、RwLock、锁顺序、Send/Sync、内存序 |
| 测试 | | 单元/集成/文档测试、快照测试、proptest、mockall、基准测试、模糊测试 |
| 性能 | | 性能分析、火焰图、克隆、栈vs堆、迭代器、内存分配 |
| Clippy与Lint检查 | | Clippy配置、核心lint规则、工作区设置、#[expect] vs #[allow] |
| 文档 | | 文档注释、rustdoc、文档lint、覆盖率检查清单 |
Quick Reference: Coding Style
快速参考:编码风格
- Prefer over
&Tunless ownership transfer is required.clone() - Use over
&str,Stringover&[T]in function parametersVec<T> - No prefix on getters:
get_notfn name()fn get_name() - Conversion naming: (cheap borrow),
as_(expensive/cloning),to_(ownership transfer)into_ - Iterator methods: /
iter()/iter_mut()into_iter() - Import ordering: -> external crates -> workspace crates ->
std->super::crate:: - Comments explain why (safety, workarounds), not what
- Use over string concatenation with
format!+ - Prefer over
s.bytes()for ASCII-only operationss.chars() - Avoid macros unless necessary; prefer functions or generics
- 优先使用而非
&T,除非需要转移所有权.clone() - 函数参数中优先使用而非
&str、优先使用String而非&[T]Vec<T> - getter方法不要加前缀:用
get_而非fn name()fn get_name() - 转换方法命名规则:(低成本借用)、
as_(高成本/克隆)、to_(所有权转移)into_ - 迭代器方法:/
iter()/iter_mut()into_iter() - 导入顺序:-> 外部 crate -> 工作区内部 crate ->
std->super::crate:: - 注释解释「为什么」(安全性、临时解决方案),而非「做了什么」
- 使用而非
format!做字符串拼接+ - 仅处理ASCII内容时优先用而非
s.bytes()s.chars() - 除非必要不要用宏,优先选择函数或泛型
Quick Reference: Error Handling
快速参考:错误处理
- Return for fallible operations; reserve
Result<T, E>for unrecoverable bugspanic! - No in production. Use
unwrap()with descriptive message only when the value is logically guaranteed. Preferexpect(),?,if letfor all other caseslet...else - Use for library/crate errors,
thiserrorfor binaries onlyanyhow - Prefer operator over
?chains for error propagationmatch - Use variants (
_else,ok_or_else) to prevent eager allocationunwrap_or_else - Use and
inspect_errfor logging and transforming errorsmap_err - at function entry for invariant checking (debug builds)
assert!
- 可失败操作返回;
Result<T, E>仅用于不可恢复的bugpanic! - 生产代码中禁止使用。仅当值逻辑上必然存在时使用带描述信息的
unwrap(),其他所有场景优先用expect()、?、if letlet...else - 库/crate的错误定义用,二进制程序用
thiserroranyhow - 错误传播优先用运算符而非
?链式调用match - 使用变体(
_else、ok_or_else)避免提前内存分配unwrap_or_else - 错误日志和转换用和
inspect_errmap_err - 函数入口用做不变性检查(仅Debug构建生效)
assert!
Quick Reference: Ownership & Pointers
快速参考:所有权与指针
- Small types (<=24 bytes, all fields
Copy, no heap) pass by valueCopy - Use when data may or may not need ownership
Cow<'_, T> - Meaningful lifetime names: ,
'src,'ctx— not just'conn'a - Use on
try_borrow()to avoid panics; prefer over directRefCell.borrow_mut() - Shadowing for transformations:
let x = x.parse()?
| Pointer | When to Use |
|---|---|
| Single ownership, heap allocation, recursive types |
| Shared ownership, single-threaded |
| Shared ownership, multi-threaded |
| Interior mutability, single-threaded |
| Interior mutability, multi-threaded |
- 小型类型(<=24字节、所有字段都是
Copy、无堆内存分配)按值传递Copy - 数据可能需要也可能不需要所有权时用
Cow<'_, T> - 生命周期命名要有意义:用、
'src、'ctx而非无意义的'conn'a - 上优先用
RefCell避免panic,不要直接调用try_borrow().borrow_mut() - 变量转换用同名遮蔽:
let x = x.parse()?
| 指针类型 | 适用场景 |
|---|---|
| 单一所有权、堆分配、递归类型 |
| 共享所有权、单线程场景 |
| 共享所有权、多线程场景 |
| 内部可变性、单线程场景 |
| 内部可变性、多线程场景 |
Quick Reference: Traits & Generics
快速参考:Trait与泛型
- Prefer generics (static dispatch) by default for zero-cost abstractions
- Use only when heterogeneous collections or plugin architectures are needed
dyn Trait - Box at API boundaries, not internally
- Object safety: no generic methods, no , methods use
Self: Sized/&self/&mut selfself - Use sealed traits to prevent external implementors
- Type state pattern encodes valid states in the type system:
rust
struct Connection<S> { _state: PhantomData<S> }
struct Disconnected;
struct Connected;
impl Connection<Connected> { fn send(&self, data: &[u8]) { /* ... */ } }- 默认优先用泛型(静态分发)实现零成本抽象
- 仅当需要异构集合或插件架构时使用
dyn Trait - Box仅在API边界使用,不要在内部逻辑使用
- 对象安全规则:无泛型方法、无约束、方法使用
Self: Sized/&self/&mut self作为参数self - 用密封trait禁止外部实现
- 类型状态模式可在类型系统中编码合法状态:
rust
struct Connection<S> { _state: PhantomData<S> }
struct Disconnected;
struct Connected;
impl Connection<Connected> { fn send(&self, data: &[u8]) { /* ... */ } }Quick Reference: Async & Concurrency
快速参考:异步与并发
- Async for I/O-bound work, sync for CPU-bound work
- Never hold locks across points — use scoped guards
.await - Never use in async — use
std::thread::sleeptokio::time::sleep - Never spawn unboundedly — use semaphores for limits
- Ensure bounds on spawned futures
Send - Use for managing multiple concurrent tasks
JoinSet - Use (from
CancellationToken) for graceful shutdowntokio_util - Instrument with +
tracingfor async debugging#[instrument]
| Channel | Use Case |
|---|---|
| Multi-producer, single-consumer message passing |
| Multi-producer, multi-consumer event fan-out |
| Single value, single use (request-response) |
| Latest-value-only, change notification |
- Sync channels: over
crossbeam::channelstd::sync::mpsc - Async channels:
tokio::sync::{mpsc, broadcast, oneshot, watch} - Atomics (,
AtomicBool) overAtomicUsizefor primitive typesMutex - Choose memory ordering carefully: /
Relaxed/Acquire/ReleaseSeqCst
- IO密集型工作用异步实现,CPU密集型工作用同步实现
- 不要在点之间持有锁,使用作用域守卫
.await - 异步代码中不要用,改用
std::thread::sleeptokio::time::sleep - 不要无限制 spawn 任务,用信号量做并发限制
- 确保 spawn 的 future 满足约束
Send - 用管理多个并发任务
JoinSet - 优雅关闭用提供的
tokio_utilCancellationToken - 异步调试用+
tracing做埋点#[instrument]
| 通道类型 | 适用场景 |
|---|---|
| 多生产者单消费者消息传递 |
| 多生产者多消费者事件广播 |
| 单次单值传递(请求-响应场景) |
| 仅传递最新值、变更通知 |
- 同步通道优先用而非
crossbeam::channelstd::sync::mpsc - 异步通道用
tokio::sync::{mpsc, broadcast, oneshot, watch} - 基础类型的同步优先用原子量(、
AtomicBool)而非AtomicUsizeMutex - 谨慎选择内存序:/
Relaxed/Acquire/ReleaseSeqCst
Quick Reference: Testing
快速参考:测试
- Name tests descriptively:
process_should_return_error_when_input_empty() - One assertion per test when possible; include formatted failure messages
- Group tests in blocks by unit of work
mod - Use doc tests () for public API examples; run separately with
///cargo test --doc - Snapshot testing: then
cargo insta test; redact unstable fieldscargo insta review - for parameterized tests with
rstestlabels#[case::name] - for property-based testing with custom strategies
proptest - with
mockallfor mocking traits#[automock] - for benchmarks with
criterionanditer_batchedBenchmarkId - with
cargo-fuzzfor fuzz testinglibfuzzer_sys - or
cargo-tarpaulinfor code coveragecargo-llvm-cov - for database integration tests with automatic pool injection
sqlx::test - Use and
#[should_panic]attributes where appropriate#[ignore]
- 测试命名要清晰描述场景:
process_should_return_error_when_input_empty() - 尽可能单测试单断言,包含格式化的失败提示信息
- 按工作单元将测试分组到块中
mod - 公共API示例用文档测试()编写,可通过
///单独运行cargo test --doc - 快照测试:执行后执行
cargo insta test;对不稳定字段做脱敏处理cargo insta review - 参数化测试用搭配
rstest标签实现#[case::name] - 属性测试用搭配自定义策略实现
proptest - Trait mock用搭配
mockall实现#[automock] - 基准测试用搭配
criterion和iter_batched实现BenchmarkId - 模糊测试用搭配
cargo-fuzz实现libfuzzer_sys - 代码覆盖率用或
cargo-tarpaulin统计cargo-llvm-cov - 数据库集成测试用实现自动连接池注入
sqlx::test - 合适场景下使用和
#[should_panic]属性#[ignore]
Quick Reference: Performance
快速参考:性能
- Golden rule: don't guess, measure. Always benchmark with
--release - Run for performance-related hints
cargo clippy -- -D clippy::perf - Use or
cargo flamegraph(macOS) for profilingsamply - Avoid cloning in loops; clone at the last moment only
- Pre-allocate: ,
Vec::with_capacity()String::with_capacity() - Prefer iterators over manual loops; avoid intermediate
for.collect() - Stack for small types, heap for large/recursive; use for large const arrays
smallvec - Use to avoid unnecessary allocation
Cow<'_, T> - Prefer over
s.bytes()for ASCII-only string operationss.chars()
- 黄金准则:不要猜测,要实测。基准测试一定要加参数运行
--release - 执行获取性能相关优化提示
cargo clippy -- -D clippy::perf - 性能分析用或
cargo flamegraph(macOS平台)samply - 避免在循环中做克隆,仅在必要的最后一步克隆
- 提前分配内存:用、
Vec::with_capacity()String::with_capacity() - 迭代器优先于手动循环;避免不必要的中间
for调用.collect() - 小型类型用栈存储,大型/递归类型用堆存储;大常量数组用优化
smallvec - 用避免不必要的内存分配
Cow<'_, T> - 仅处理ASCII内容时优先用而非
s.bytes()s.chars()
Quick Reference: Clippy & Linting
快速参考:Clippy与Lint检查
Run regularly:
cargo clippy --all-targets --all-features --locked -- -D warnings| Lint | Catches |
|---|---|
| Unnecessary |
| Unnecessary |
| Oversized variants (consider |
| Premature |
| |
| Functions always returning |
| |
- Use over
#[expect(clippy::lint)]—#[allow(...)]warns when lint no longer appliesexpect - Add justification comment on every suppression
- Set as workspace minimum
#![warn(clippy::all)] - Configure workspace lints in with priority levels
Cargo.toml
定期执行:
cargo clippy --all-targets --all-features --locked -- -D warnings| Lint规则 | 检测内容 |
|---|---|
| 不必要的 |
| 不必要的 |
| 过大的枚举变体(建议用 |
| 迭代前提前执行 |
| |
| 函数总是返回 |
| 对 |
- 优先用而非
#[expect(clippy::lint)]——当lint规则不再触发时#[allow(...)]会给出警告expect - 每一条lint抑制都要加注释说明理由
- 工作区最低配置为
#![warn(clippy::all)] - 在中配置工作区lint规则和优先级
Cargo.toml
Quick Reference: Documentation
快速参考:文档
- comments explain why: safety invariants, workarounds, design rationale
// - doc comments explain what and how for all public items
/// - for module-level and crate-level documentation at top of
//!/lib.rsmod.rs - Every needs a linked issue:
TODO// TODO(#42): description - Enable for libraries
#![deny(missing_docs)] - Include ,
# Examples,# Errors,# Panicssections in doc comments# Safety
| Doc Lint | Purpose |
|---|---|
| Ensure all public items documented |
| Catch dead cross-references |
| Document panic conditions |
| Document error conditions |
| Document unsafe safety requirements |
- 注释解释「为什么」:安全不变性、临时解决方案、设计 rationale
// - 文档注释解释所有公共项的「是什么」和「怎么用」
/// - 模块级和crate级文档用写在
//!/lib.rs顶部mod.rs - 每个都要关联issue:
TODO// TODO(#42): 描述 - 库代码启用校验
#![deny(missing_docs)] - 文档注释中包含、
# 示例、# 错误、# Panic章节# 安全性
| 文档Lint规则 | 用途 |
|---|---|
| 确保所有公共项都有文档 |
| 检测失效的内部文档链接 |
| 要求说明panic触发条件 |
| 要求说明错误触发条件 |
| 要求说明unsafe代码的安全约束 |
Quick Reference: Data Types & Patterns
快速参考:数据类型与模式
- Use newtypes for domain semantics:
struct Email(String) - Prefer slice patterns:
if let [first, .., last] = slice - Use arrays for fixed sizes; avoid when length is known at compile time
Vec - Shadowing for transformation:
let x = x.parse()? - when data might need modification of borrowed data
Cow<str> - on strings is O(n*m) — avoid nested string iteration
contains()
- 领域语义用新类型实现:
struct Email(String) - 优先用切片模式:
if let [first, .., last] = slice - 固定长度的集合用数组,编译时已知长度时不要用
Vec - 变量转换用同名遮蔽:
let x = x.parse()? - 可能需要修改借用数据时用
Cow<str> - 字符串是O(n*m)复杂度,避免嵌套字符串遍历
contains()
Deprecated to Modern Migration
弃用特性迁移指南
| Deprecated | Better | Since |
|---|---|---|
| | Rust 1.70 |
| | Rust 1.80 |
| | — |
| | — |
| | — |
| | Rust 2018 |
| Native | Rust 1.75 |
| 弃用用法 | 推荐替代 | 起始版本 |
|---|---|---|
| | Rust 1.70 |
| | Rust 1.80 |
| | — |
| | — |
| | — |
| | Rust 2018 |
| trait原生 | Rust 1.75 |
Cargo.toml Essentials
Cargo.toml核心配置
Recommended dependencies:
toml
[dependencies]
thiserror = "2"
anyhow = "1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = "0.3"
[dev-dependencies]
rstest = "0.25"
proptest = "1"
mockall = "0.13"
criterion = { version = "0.5", features = ["html_reports"] }
insta = { version = "1", features = ["yaml"] }Workspace lints ():
Cargo.tomltoml
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }rustfmt.tomltoml
reorder_imports = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"推荐依赖:
toml
[dependencies]
thiserror = "2"
anyhow = "1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = "0.3"
[dev-dependencies]
rstest = "0.25"
proptest = "1"
mockall = "0.13"
criterion = { version = "0.5", features = ["html_reports"] }
insta = { version = "1", features = ["yaml"] }工作区Lint配置():
Cargo.tomltoml
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }rustfmt.tomltoml
reorder_imports = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"Constraints
约束规则
MUST DO
必须遵守
- Use ownership and borrowing for memory safety
- Handle all errors explicitly via /
Result— no silent failuresOption - Use for library errors,
thiserrorfor binariesanyhow - Minimize code; document all
unsafeblocks with safety invariantsunsafe - Use the type system for compile-time guarantees
- Run and fix all warnings
cargo clippy - Use for consistent formatting
cargo fmt - Write tests including doc tests for public APIs
- Add documentation with examples for all public items
/// - Use for observability in async code
tracing - When reviewing or writing code, suggest a testing approach using the recommended tools (,
rstest,proptest,insta,mockall) — even if the user did not ask for testscriterion
- 使用所有权和借用保证内存安全
- 所有错误都通过/
Result显式处理——禁止静默失败Option - 库错误用定义,二进制程序用
thiserror处理错误anyhow - 尽量减少代码;所有
unsafe块都要加注释说明安全不变性unsafe - 利用类型系统实现编译时校验
- 执行并修复所有警告
cargo clippy - 用保证代码格式统一
cargo fmt - 编写测试,包含公共API的文档测试
- 所有公共项都要有带示例的文档
/// - 异步代码用实现可观测性
tracing - 审查或编写代码时,即使用户没有要求测试,也要给出基于推荐工具(、
rstest、proptest、insta、mockall)的测试方案建议criterion
MUST NOT DO
禁止操作
- Use in production code
unwrap() - Create memory leaks or dangling pointers
- Use without documented safety invariants
unsafe - Ignore clippy warnings without and justification
#[expect(...)] - Hold locks across points
.await - Use in async context
std::thread::sleep - Skip error handling or use for recoverable errors
panic! - Use where
Stringsuffices; clone unnecessarily&str - Spawn tasks unboundedly without semaphore limits
- Mix blocking and async code without
spawn_blocking
- 生产代码中使用
unwrap() - 造成内存泄漏或悬垂指针
- 使用但不标注安全不变性说明
unsafe - 忽略clippy警告,且没有标注和理由说明
#[expect(...)] - 在点之间持有锁
.await - 在异步上下文使用
std::thread::sleep - 跳过错误处理,或用处理可恢复错误
panic! - 可用的场景用
&str;做不必要的克隆String - 没有信号量限制就无限制 spawn 任务
- 不使用就混合阻塞代码和异步代码
spawn_blocking