rust-systems
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust Systems & Services
Rust系统与服务
Covers modern application-layer Rust (edition 2024): CLIs, web services, libraries. Not /embedded.
no_std涵盖现代应用层Rust(2024版本):CLI、Web服务、库。不包含/嵌入式开发内容。
no_stdTooling
工具链
| Tool | Purpose |
|---|---|
| Build, dep management, script runner |
| Lint ( |
| Formatter ( |
| Test runner |
| License + advisory + duplicate-dep checks |
| Find unused dependencies |
- Pin per repo so every contributor and CI uses the same compiler.
rust-toolchain.toml - for single-package upgrades.
cargo update -p <crate>rewrites everything — avoid in PR diffs.cargo update - goes in version control for binaries and libraries (modern guidance; reproducibility wins).
Cargo.lock
| 工具 | 用途 |
|---|---|
| 构建、依赖管理、脚本运行器 |
| 代码检查( |
| 代码格式化( |
| 测试运行器 |
| 许可证、安全预警、重复依赖检查 |
| 查找未使用的依赖 |
- 每个仓库固定,确保所有贡献者和CI使用相同版本的编译器。
rust-toolchain.toml - 使用升级单个包。
cargo update -p <crate>会更新所有依赖——在PR差异中避免使用该命令。cargo update - 二进制文件和库的都应纳入版本控制(现代最佳实践;可复现性优先)。
Cargo.lock
Workspaces
工作区
Multi-crate projects use a workspace with layered crates. Dependencies point inward only.
Cargo.toml # [workspace] members + [workspace.dependencies]
crates/
protocol/ # Shared types, no deps on other workspace crates
storage/ # Persistence, depends on protocol
service/ # Business logic, depends on protocol + storage
cli/ # Binary, depends on everything-
Centralize versions in, reference as
[workspace.dependencies]in members.foo = { workspace = true } -
Keep the leaf-most crate (/ types) dependency-free so every other crate can depend on it without cycles.
protocol -
Feature flags belong on the crate that introduces the dependency, not re-exported through the workspace root.
-
Library crates expose one stable facade: a thinwith a
lib.rspurpose doc and//!re-exports — one import path per concept, internals free to reorganize without breaking callers.pub use -
alone does not prove an item is externally reachable. Reachability runs through the re-export graph: a
pubitem inside a private module that is never re-exported is free to change, while the same item surfaced through apubat the crate root is not — even though its containing module stays private. (Apub useitem cannot be re-exported outside the crate:pub(crate)on one ispub use, whileE0364compiles.) Trace the facade before calling a reorganization internal. On a library crate with a published baseline,pub(crate) usesettles it mechanically.cargo semver-checks -
Defining aor proc macro, or handling paths, process output, or on-disk state? Load macros-and-os-boundaries.md —
macro_rules!resolution, single-interpolation of$crate,$x:exprprecedence, item-name collisions across invocations,$t:ttover panic, non-UTF-8syn::Error/Path, and write-then-rename. These type-check cleanly and fail on a caller's machine.OsStr -
Document public items at the point of exposure.on every public item (purpose, params, return, plus
////# Examples/# Errors/# Panicswhere they apply);# Safetyfor modules and crates. Doc examples compile and run under//!, so they are regression tests, not decoration. Enforce withcargo test --docon library crates; see rustdoc.md.#![deny(missing_docs)] -
Feature gates must error, never silently degrade. If runtime config requests a capability the binary wasn't compiled with (e.g.on a non-CUDA build), fail at startup — silent fallback diverges from operator config unnoticed.
device = "gpu" -
Centralize lints at the workspace root with— every member crate inherits the same ruleset, no per-crate
[workspace.lints.*]drift:#![deny(...)]toml[workspace.lints.clippy] all = { level = "warn", priority = -1 } pedantic = { level = "warn", priority = -1 }Each member crate opts in with.[lints] workspace = true
多crate项目使用分层crate的工作区,依赖仅向内引用。
Cargo.toml # [workspace]成员 + [workspace.dependencies]
crates/
protocol/ # 共享类型,不依赖工作区中的其他crate
storage/ # 持久化模块,依赖protocol
service/ # 业务逻辑,依赖protocol + storage
cli/ # 二进制程序,依赖所有模块-
在中集中管理版本,成员crate中通过
[workspace.dependencies]引用。foo = { workspace = true } -
最底层的crate(/类型模块)应无依赖,确保其他所有crate都能依赖它而不产生循环。
protocol -
特性标志应添加在引入依赖的crate上,而非通过工作区根目录重新导出。
-
库crate需暴露一个稳定的对外接口:简洁的包含
lib.rs用途文档和//!重导出——每个概念对应一个导入路径,内部结构可自由调整而不破坏调用方代码。pub use -
仅修饰并不代表项可被外部访问。可访问性需通过重导出链判断:私有模块内的
pub项若从未被重导出,则可自由修改;而通过crate根目录pub暴露的同一项则不可修改——即使其所在模块仍为私有。(pub use项无法被导出到crate外部:对其使用pub(crate)会触发pub use错误,而E0364可正常编译。)在调整内部结构前,先追踪对外接口。对于已发布基线的库crate,可使用pub(crate) use自动检查版本兼容性。cargo semver-checks -
定义或过程宏,或处理路径、进程输出、磁盘状态? 参考macros-and-os-boundaries.md——包含
macro_rules!解析、$crate单次插值、$x:expr优先级、调用间的项名冲突、$t:tt替代panic、非UTF-8的syn::Error/Path、先写后重命名等内容。这些内容可通过类型检查,且会在调用方机器上明确报错。OsStr -
在暴露点为公共项添加文档。每个公共项都需添加注释(说明用途、参数、返回值,必要时添加
////# Examples/# Errors/# Panics);模块和crate需添加# Safety注释。文档示例可通过//!编译运行,因此它们是回归测试而非装饰。在库crate中通过cargo test --doc强制要求文档;参考rustdoc.md。#![deny(missing_docs)] -
特性标志必须报错,而非静默降级。若运行时配置请求了二进制未编译的功能(例如非CUDA构建中设置),应在启动时失败——静默回退会导致实际行为与操作员配置不一致且未被察觉。
device = "gpu" -
在工作区根目录集中管理代码检查规则,通过配置——所有成员crate继承相同规则集,避免每个crate单独设置
[workspace.lints.*]导致规则不一致:#![deny(...)]toml[workspace.lints.clippy] all = { level = "warn", priority = -1 } pedantic = { level = "warn", priority = -1 }每个成员crate通过启用继承。[lints] workspace = true
Build Profiles
构建配置文件
When tuning Cargo build profiles (release LTO, release-dbg symbols, release-min for distributable binaries) or adding dev-machine speedups (mold linker, , share-generics), load build-profiles.md.
target-cpu=native当调整Cargo构建配置文件(发布版本LTO、发布版本调试符号、用于分发二进制文件的最小化发布版本)或添加开发机器加速配置(mold链接器、、共享泛型)时,参考build-profiles.md。
target-cpu=nativeError Handling
错误处理
Split by crate role:
- Libraries / lower crates: define typed errors with . Consumers can pattern-match.
thiserror - Binaries / top-level crates: use with
anyhow::Result. Human-readable error chains..context("what was being attempted") - Never return from library APIs — it erases variant information.
Box<dyn Error> - Use liberally. Never
?or.unwrap()outside tests and.expect(). Anmainis acceptable only when the invariant is provably upheld and the message explains why.expect("...") - Convert at boundaries: on thiserror variants for auto-conversion;
#[from]when explicit..map_err(MyError::from) - /
bail!("...")in application code for early exits.ensure!(cond, "...") - Prefer over panics for any recoverable error. Panics are for programmer bugs (broken invariants), not runtime failures.
Result<T, E> - on fallible APIs: annotate functions returning
#[must_use]or newtype-wrapped results that callers frequently ignore. CatchesResultat compile time instead of shipping a silently-dropped error.let _ = validate(x); - Make illegal call-sequences unrepresentable — the type-state pattern: encode a mandatory call order as distinct types (→
Client<Uninitialized>) so an out-of-order call fails to compile instead of erroring at runtime.Client<Connected>
按crate角色划分:
- 库/底层crate:使用定义类型化错误,便于消费者进行模式匹配。
thiserror - 二进制程序/顶层crate:使用并配合
anyhow::Result,生成人类可读的错误链。.context("当前操作描述") - 库API绝不要返回——这会丢失变体信息。
Box<dyn Error> - 大量使用操作符。除测试和
?函数外,绝不要使用main或.unwrap()。只有当不变量可被证明成立且注释说明原因时,.expect()才是可接受的。expect("...") - 在边界处转换错误:在thiserror变体上使用实现自动转换;需要显式转换时使用
#[from]。.map_err(MyError::from) - 在应用代码中使用/
bail!("...")实现提前退出。ensure!(cond, "...") - 对于可恢复错误,优先使用而非panic。panic仅用于程序员错误(破坏不变量),而非运行时故障。
Result<T, E> - 对易出错的API添加:对返回
#[must_use]或调用方常忽略的新类型包装结果的函数添加该注解。在编译时捕获Result这类代码,避免错误被静默丢弃。let _ = validate(x); - 使非法调用序列无法被表示——类型状态模式:将强制调用顺序编码为不同类型(→
Client<Uninitialized>),使顺序错误的调用在编译阶段失败而非运行时报错。Client<Connected>
Ownership Discipline
所有权规范
- Take over
&str,&Stringover&[T]in function signatures — accepts more call sites for free.&Vec<T> - Return owned (,
String) from constructors and public APIs. Borrow in hot paths where lifetimes are obvious.Vec<T> - Reach for only when sharing across threads. Single-threaded sharing uses
Arc<T>or references.Rc<T> - when a function sometimes allocates and sometimes borrows (e.g. normalization).
Cow<'_, str> - Rely on lifetime elision. More than one signature needing an explicit is a signal the type should own its data — convert the borrow to owned before adding lifetimes.
'a - Reducing hot-path allocations (SmallVec, ArrayVec, string interning, , vectored writes): profile first, then load performance.md.
Bytes
- 函数签名中优先使用而非
&str,&String而非&[T]——可兼容更多调用场景。&Vec<T> - 构造函数和公共API返回所有权类型(、
String)。在生命周期明确的热点路径中使用借用。Vec<T> - 仅在线程间共享数据时使用。单线程共享使用
Arc<T>或引用。Rc<T> - 当函数有时需要分配内存、有时可借用数据时(例如归一化操作),使用。
Cow<'_, str> - 依赖生命周期省略规则。若多个签名需要显式,则表明该类型应拥有自身数据——在添加生命周期前将借用转换为所有权类型。
'a - 减少热点路径的内存分配(SmallVec、ArrayVec、字符串驻留、、向量写入):先进行性能分析,再参考performance.md。
Bytes
Async with Tokio
基于Tokio的异步编程
- Default runtime: with
#[tokio::main]for apps;features = ["full"]for libraries that need to stay slim.features = ["rt", "macros", "sync"] - for independent tasks.
tokio::spawnfor a dynamic group awaited together with cancellation.JoinSet - for racing futures (timeouts, cancellation, first-wins).
tokio::select! - Never block the runtime: for sync CPU work or blocking I/O libs.
tokio::task::spawn_blocking - only when the guard must be held across
tokio::sync::Mutex. Otherwise.awaitis faster.std::sync::Mutex - when reads dominate writes (config snapshots, route tables, hot caches). Many readers proceed in parallel;
tokio::sync::RwLockserializes them. For snapshot-swap semantics (rarely-updated config),Mutexis faster still — no lock on the read path.arc-swap::ArcSwap - Cancellation: (from
CancellationToken) propagates shutdown. Long-running tasks must check it.tokio-util - Backpressure via bounded channels — unbounded channels hide memory growth until OOM.
mpsc - for hard concurrency limits on spawn paths that don't fit a channel model (e.g. "at most 50 concurrent outbound HTTP calls").
Semaphoreinside the task; dropping the permit releases the slot. Pair withlet _permit = sem.acquire().await?;shared across spawners.Arc<Semaphore> - Don't mix async runtimes. Pick and stick with it;
tokioandasync-stddon't interop cleanly.smol
- 默认运行时:应用程序使用并启用
#[tokio::main];需保持轻量的库使用features = ["full"]。features = ["rt", "macros", "sync"] - 使用创建独立任务。使用
tokio::spawn管理动态任务组,可一起等待并支持取消。JoinSet - 使用实现future竞争(超时、取消、先完成优先)。
tokio::select! - 绝不要阻塞运行时:对于同步CPU密集型工作或阻塞I/O库,使用。
tokio::task::spawn_blocking - 仅当需要在期间持有锁时使用
.await。否则tokio::sync::Mutex速度更快。std::sync::Mutex - 当读操作远多于写操作时使用(配置快照、路由表、热点缓存)。多个读操作可并行执行;
tokio::sync::RwLock会序列化所有操作。对于快照交换语义(极少更新的配置),Mutex速度更快——读路径无锁。arc-swap::ArcSwap - 取消机制:使用(来自
CancellationToken)传播关闭信号。长时间运行的任务必须检查该信号。tokio-util - 通过有界通道实现背压——无界通道会隐藏内存增长直至发生OOM。
mpsc - 使用实现硬并发限制,适用于不适合通道模型的任务创建场景(例如“最多50个并发出站HTTP调用”)。在任务内部执行
Semaphore;释放permit会归还槽位。配合let _permit = sem.acquire().await?;在多个任务创建者间共享。Arc<Semaphore> - 不要混合使用异步运行时。选择并坚持使用;
tokio和async-std无法良好互操作。smol
CLI Tools (clap)
CLI工具(clap)
- Use the derive API: +
#[derive(Parser)]. Less boilerplate, types drive the help text.#[derive(Subcommand)] - One variant per subcommand; flatten shared flags into a
enum Commands.#[command(flatten)] struct CommonArgs - flag on query commands for agent/pipe consumption. Emit via
--json.serde_json::to_string(&value)? - Exit codes: 0 success, 1 for errors returned, 2 for argparse (clap handles this), reserve 3+ for domain meanings documented in
main.--help - Provide automatically via
--version.#[command(version)]
See cli-tools.md for config layering, logging setup, progress reporting, and shell completions.
- 使用派生API:+
#[derive(Parser)]。更少样板代码,类型驱动帮助文本生成。#[derive(Subcommand)] - 每个子命令对应一个变体;将共享标志整合到
enum Commands中。#[command(flatten)] struct CommonArgs - 查询命令添加标志,便于Agent/管道消费。通过
--json输出。serde_json::to_string(&value)? - 退出码:0表示成功,1表示返回的错误,2表示参数解析错误(clap自动处理),3及以上保留给领域特定含义并在
main中说明。--help - 通过自动提供
#[command(version)]功能。--version
参考cli-tools.md了解配置分层、日志设置、进度报告和Shell补全。
HTTP Services (axum)
HTTP服务(axum)
- Framework default: axum (tokio-native, tower middleware, extractor-based handlers). Pick only if an existing codebase uses it.
actix-web - Handlers return . Implement
Result<impl IntoResponse, AppError>forIntoResponseto centralize error → status mapping.AppError - Validate input at the boundary: where
axum::extract::Json<T>(useT: Deserialize + Validatecrate). Internal services trust input was validated.validator - Share state via — not globals, not
State<Arc<AppState>>.lazy_static - Middleware via : tracing → timeout → auth → CORS → handler. Order matters.
tower::ServiceBuilder - Resilience layers (outbound clients, shared services): combine +
LoadShedfor backpressure, not unbounded queueing; full tower stack in production-resilience.md.ConcurrencyLimit
See axum-service.md for project layout, extractors, error types, graceful shutdown, and OpenAPI generation.
- 默认框架:axum(基于tokio、tower中间件、提取器式处理器)。仅当现有代码库使用时才选择它。
actix-web - 处理器返回。为
Result<impl IntoResponse, AppError>实现AppError以集中处理错误→状态码映射。IntoResponse - 在边界处验证输入:使用,其中
axum::extract::Json<T>(使用T: Deserialize + Validatecrate)。内部服务信任输入已被验证。validator - 通过共享状态——不使用全局变量或
State<Arc<AppState>>。lazy_static - 通过添加中间件:tracing → 超时 → 认证 → CORS → 处理器。顺序至关重要。
tower::ServiceBuilder - 弹性层(出站客户端、共享服务):结合+
LoadShed实现背压,避免无界排队;完整的tower栈参考production-resilience.md。ConcurrencyLimit
参考axum-service.md了解项目布局、提取器、错误类型、优雅关闭和OpenAPI生成。
Concurrency
并发
| Workload | Approach |
|---|---|
| Independent async I/O | |
| Data-parallel CPU work | |
| Shared mutable state across threads | |
| Single-producer pipelines | |
| Broadcast / fan-out | |
rayontokiotokio::task::spawn_blocking.block_on()| 工作负载 | 实现方式 |
|---|---|
| 独立异步I/O | |
| 数据并行CPU工作 | |
| 线程间共享可变状态 | |
| 单生产者流水线 | |
| 广播/扇出 | |
rayontokiotokio::task::spawn_blocking.block_on()Testing
测试
- Built-in . Prefer
#[test]overcargo nextest run --workspace— it runs tests in parallel processes with proper isolation.cargo test - Unit tests live in at the bottom of the file (access to private items).
mod tests { ... } - Integration tests in directory. One file per public surface area.
tests/ - for async tests. Add
#[tokio::test]when the code under test spawns tasks.flavor = "multi_thread" - for parametrized tests and fixtures.
rstest/proptestfor property-based tests on pure logic.quickcheck - for snapshot testing CLI output, serialization, large structs. Review diffs with
insta.cargo insta review - +
assert_cmdfor CLI integration tests (invokes the binary, asserts on stdout/stderr/exit code).predicates - Assert on error variants with :
matches!— noassert!(matches!(result.unwrap_err(), MyError::Validation(_)))arms to update when unrelated variants are added.match - Coverage: . Target 70%+ on application code, higher on library crates.
cargo llvm-cov --workspace --html - Fuzzing for parsers: +
cargo fuzzon any code parsing untrusted input; nightly runs surface panics and UB unit tests miss.libfuzzer-sys
For generic test discipline (anti-patterns, mock rules, rationalization resistance), see the skill.
ia-writing-tests- 使用内置的。优先使用
#[test]而非cargo nextest run --workspace——它在并行进程中运行测试并提供适当的隔离。cargo test - 单元测试放在文件底部的中(可访问私有项)。
mod tests { ... } - 集成测试放在目录中,每个文件对应一个公共接口。
tests/ - 异步测试使用。当被测代码会创建任务时,添加
#[tokio::test]。flavor = "multi_thread" - 使用实现参数化测试和夹具。使用
rstest/proptest对纯逻辑进行基于属性的测试。quickcheck - 使用进行快照测试,适用于CLI输出、序列化、大型结构体。使用
insta查看差异。cargo insta review - 使用+
assert_cmd进行CLI集成测试(调用二进制程序,断言标准输出/标准错误/退出码)。predicates - 使用断言错误变体:
matches!——添加无关变体时无需更新match分支。assert!(matches!(result.unwrap_err(), MyError::Validation(_))) - 覆盖率:。应用代码目标覆盖率70%+,库crate目标覆盖率更高。
cargo llvm-cov --workspace --html - 解析器模糊测试:对处理非可信输入的代码使用+
cargo fuzz;nightly版本运行可发现单元测试遗漏的panic和UB。libfuzzer-sys
通用测试规范(反模式、mock规则、抗合理化)参考技能。
ia-writing-testsUnsafe Discipline
Unsafe代码规范
- Default: no . If clippy flags it, don't
unsafeit — refactor. The#[allow]escape hatch below does not apply here; unsafe findings get fixed, not annotated.#[expect] - Every block gets a
unsafecomment above it explaining why each invariant holds. No comment = reviewer rejects.// SAFETY: - Keep blocks minimal — wrap in a safe abstraction at module boundary, mark the module
unsafe.pub(crate) - Use (
miri) on any crate containingcargo +nightly miri testor raw pointer arithmetic — catches UB that optimizers mask.unsafe - Prefer ,
bytemuck,zerocopyover hand-rolled transmutes for zero-copy patterns.bytes - Env-var writes are in edition 2024. Write them only in
unsafe, before the runtime starts or any thread spawns. Concurrentmainis UB;getenvdoes not make it safe. Watch for lazyOnceLock-style writes on first use — hoist them to startup.LD_LIBRARY_PATH
- 默认:不使用。若clippy标记了问题,不要使用
unsafe——重构代码。下面的#[allow]逃生舱不适用于此;unsafe代码问题必须修复,而非注解。#[expect] - 每个块上方必须添加
unsafe注释,说明每个不变量成立的原因。无注释则评审者应拒绝。// SAFETY: - 尽可能缩小块的范围——在模块边界处包装为安全抽象,并将模块标记为
unsafe。pub(crate) - 对包含或原始指针算术的crate使用
unsafe(miri)——捕获优化器掩盖的UB。cargo +nightly miri test - 对于零拷贝模式,优先使用、
bytemuck、zerocopy而非手动实现transmute。bytes - 在2024版本中,写入环境变量是操作。仅在
unsafe函数中写入,且需在运行时启动或任何线程创建之前完成。 并发main是UB;getenv无法使其安全。注意首次使用时延迟写入OnceLock这类环境变量——将其提前到启动阶段。LD_LIBRARY_PATH
Production Resilience
生产环境弹性
When productionizing a service (config validation, + endpoints, graceful shutdown, retries/timeouts/jitter, deny-by-default fallback when the call is the security decision, connection pools, diagnostic secret redaction), load production-resilience.md.
/health/ready当将服务投入生产环境时(配置验证、+端点、优雅关闭、重试/超时/抖动、调用时默认拒绝的安全决策、连接池、诊断信息脱敏),参考production-resilience.md。
/health/readyObservability
可观测性
For logging ( + with init recipe), spans, correlation IDs, metrics, and distributed tracing patterns, load observability.md. Never use or in new code.
tracingtracing-subscriber#[instrument]println!log::日志( + 初始化方案)、跨度、关联ID、指标和分布式追踪模式参考observability.md。新代码中绝不要使用或。
tracingtracing-subscriber#[instrument]println!log::CI
CI
General CI design lives with the agent. For Rust-specific callouts (, , , , matrix coverage guidance, doc-test step), load ci-pipeline.md.
ia-infrastructure-engineerrustsec/audit-checkcargo-llvm-covSwatinem/rust-cachetaiki-e/install-action通用CI设计参考Agent。Rust特定注意事项(、、、、矩阵覆盖率指导、文档测试步骤)参考ci-pipeline.md。
ia-infrastructure-engineerrustsec/audit-checkcargo-llvm-covSwatinem/rust-cachetaiki-e/install-actionDiscipline
开发规范
- Simplicity first — every change as simple as possible, impact minimal code.
- Only touch what's necessary — avoid unrelated changes in a PR.
- No as a shortcut — fix the underlying issue. When a suppression is genuinely warranted, write
#[allow(clippy::...)]instead:#[expect(clippy::lint_name, reason = "...")]warns once the lint stops firing, so a suppression that has outlived its cause reports itself, whereexpectrots silently forever. (allowneeds Rust 1.81+; edition 2024 clears that floor.)expect - Before adding a trait or generic, verify it's used in 3+ places. Otherwise a concrete type is clearer.
- 优先保持简洁——每个变更尽可能简单,影响代码范围最小。
- 仅修改必要内容——避免在PR中包含无关变更。
- 不要使用走捷径——修复根本问题。当确实需要抑制时,使用
#[allow(clippy::...)]替代:#[expect(clippy::lint_name, reason = "...")]会在规则不再触发时发出警告,因此失效的抑制会自动报告,而expect会静默失效。(allow需要Rust 1.81+;2024版本满足该要求。)expect - 在添加 trait 或泛型之前,确认其已在3个以上场景中使用。否则具体类型更清晰。
Verify
验证
- passes with zero diffs
cargo fmt --all -- --check - passes
cargo clippy --workspace --all-targets --all-features -- -D warnings - (or
cargo nextest run --workspace) passes with zero failurescargo test --workspace - passes (licenses, advisories, duplicates) for any crate going to production
cargo deny check - No new without
unsafecomment// SAFETY:
- 通过,无差异
cargo fmt --all -- --check - 通过
cargo clippy --workspace --all-targets --all-features -- -D warnings - (或
cargo nextest run --workspace)通过,无失败cargo test --workspace - 投入生产环境的crate需通过(许可证、安全预警、重复依赖)
cargo deny check - 新增代码必须带有
unsafe注释// SAFETY: