# rust-dev > 在处理 Rust 代码、审查 Rust 代码、管理 Rust 依赖项、创建 Rust 项目或修复 Rust 编译错误时,应使用此技能。它提供严格的编码标准(特别是 FAIL FAST 错误处理)、工作空间架构指导、依赖项管理自动化和常见 Rust 模式。 - Author: Claude Code - Repository: ZoneCNH/review-loop - Version: 20260127060541 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-06 - Source: https://github.com/ZoneCNH/review-loop - Web: https://mule.run/skillshub/@@ZoneCNH/review-loop~rust-dev:20260127060541 --- --- name: rust-dev description: 在处理 Rust 代码、审查 Rust 代码、管理 Rust 依赖项、创建 Rust 项目或修复 Rust 编译错误时,应使用此技能。它提供严格的编码标准(特别是 FAIL FAST 错误处理)、工作空间架构指导、依赖项管理自动化和常见 Rust 模式。 --- # Rust 开发 ## 概述 此技能使您能够遵循严格的标准和最佳实践编写正确、惯用的 Rust 代码。它强制执行 FAIL FAST 错误处理,正确管理依赖项,提供工作空间模板,并提供常见 Rust 模式和陷阱的解决方案。 ## 何时使用此技能 为以下情况激活此技能: - 添加或更新 Rust 依赖项 - 创建新的 Rust 项目或工作空间 - 编写或修改 Rust 代码 - 修复编译错误、借用检查器问题或生命周期问题 - 实现错误处理 - 设置 async/await 模式 - 配置 CLI 应用程序 - 组织测试和配置 - 拆分大型模块或重新组织文件结构 ## 核心标准 **在所有 Rust 代码中遵循的关键原则:** 1. **Edition 2024**:在 Cargo.toml 中始终使用 `edition = "2024"` 2. **FAIL FAST 错误处理**:绝不吞没错误 - 始终使用 `?` 或显式返回进行传播 3. **依赖项版本控制**:使用 `x.x` 格式(例如 `serde = "1.0"`) 4. **工作空间架构**:使用带有单一责任 crate 的工作空间 5. **错误类型**:库使用 thiserror(带回溯),二进制/测试使用 anyhow 6. **CLI-First 配置**:绝不绕过 CLI 参数解析 7. **测试中不使用 env::set_var**:通过函数参数传递配置 8. **异步运行时**:一致使用 tokio 有关所有标准的完整详细信息,请参阅 `references/standards.md`。 ## 依赖项管理 ### 添加依赖项 添加依赖项时: 1. **使用捆绑脚本查找最新版本**: ```bash python3 scripts/check_crate_version.py ``` 2. **使用脚本输出的 x.x 版本格式**: ```toml serde = "1.0" ``` 3. **对于工作空间项目**,添加到根 Cargo.toml 中的 `[workspace.dependencies]`: ```toml [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } ``` 4. **在成员 crate 中**,引用工作空间依赖项: ```toml [dependencies] serde = { workspace = true } ``` ### 常见依赖项 - **错误处理**:`thiserror = "1.0"`(库),`anyhow = "1.0"`(二进制/测试) - **异步**:`tokio = { version = "1.40", features = ["full"] }` - **序列化**:`serde = { version = "1.0", features = ["derive"] }` - **CLI**:`clap = { version = "4.5", features = ["derive"] }` - **派生**:`derive_more = { version = "1.0", features = ["full"] }` ## 创建新项目 ### 工作空间结构 使用 `assets/workspace-template/` 中的模板作为起点: ``` project/ ├── Cargo.toml # 工作空间根目录,无代码 ├── project/ # 库 crate(核心逻辑) │ ├── Cargo.toml # 使用 thiserror │ └── src/lib.rs └── project-cli/ # 二进制 crate(CLI 接口) ├── Cargo.toml # 使用 anyhow + clap └── src/main.rs ``` **创建新工作空间的步骤:** 1. 复制模板目录结构 2. 将 `project` 和 `project-cli` 重命名为实际项目名称 3. 更新 Cargo.toml 文件中的所有包名称 4. 更新 `project-cli/Cargo.toml` 中的二进制名称 5. 确保所有 Cargo.toml 文件指定 `edition = "2024"` ## 错误处理工作流 **这是最关键的标准 - 违反是不可接受的。** ### 规则:FAIL FAST - 绝不吞没错误 **为什么这很重要:** - 静默失败会破坏数据并使系统处于未定义状态 - 半完成的操作比崩溃更糟糕(更难调试、数据不一致) - 错误级联:一个被吞没的错误会在下游导致 10 个神秘失败 - 不传播的日志记录会给人错误信心,认为错误已"处理" **规则:** 每个错误必须传播到调用堆栈顶部。程序在错误时停止。 **✅ 正确 - 始终传播错误:** ```rust // 最佳:使用 ? 运算符 operation()?; // 带上下文:添加上下文并传播 operation().context("failed during initialization")?; // 记录用于可观察性并传播(两者都需要!) let result = operation().map_err(|e| { tracing::error!("Operation failed: {e}"); e })?; // 需要时显式匹配 match operation() { Ok(val) => process(val), Err(e) => return Err(e.into()), } ``` **❌ 禁止 - 这些都吞没错误:** ```rust if let Err(e) = operation() { log::error!("{e}"); } // 没有返回! operation().unwrap_or_default(); // 静默回退 operation().ok(); // 丢弃错误 let _ = operation(); // 显式忽略 ``` **自检:** 如果您看到 `if let Err` 或 `match ... Err` 没有 `return Err` 或 `?`,那就是错误。 **不是"错误处理":** 添加日志记录不是修复/处理错误。错误必须传播。 ### 选择错误类型 **对于库 crate**(在 src/lib.rs 或模块中)- 使用带回溯的 thiserror: ```rust use std::backtrace::Backtrace; use thiserror::Error; #[derive(Error, Debug)] pub enum MyError { #[error("IO error: {0}")] Io(#[from] std::io::Error, Backtrace), #[error("Parse error: {0}")] Parse(String, Backtrace), } pub type Result = std::result::Result; ``` **对于二进制文件**(在 main.rs 中): ```rust use anyhow::{Context, Result}; #[tokio::main] async fn main() -> Result<()> { let config = load_config() .context("Failed to load configuration")?; Ok(()) } ``` 有关更多错误处理示例,请参阅 `references/common-patterns.md`。 ## 常见模式和解决方案 遇到常见 Rust 挑战时,请参阅 `references/common-patterns.md` 获取解决方案: - **生命周期问题**:常见结构体生命周期模式和省略规则 - **Async/await**:Tokio 运行时设置,异步特征(Rust 1.75+ 中的 AFIT) - **特征对象**:动态调度、Box、Send + Sync - **配置**:使用 clap 的 CLI-first 模式 - **测试**:避免 env::set_var 的模式 - **派生**:常见宏组合 - **Newtypes**:带 derive_more 的类型安全模式 - **构建器**:使用 derive_builder **需要时搜索参考文件**以获取特定模式。 ## CLI 应用程序模式 所有 CLI 应用程序都应遵循此模式: ```rust use anyhow::{Context, Result}; use clap::Parser; #[derive(Parser, Debug)] struct CliArgs { #[arg(long, env = "API_KEY")] api_key: String, } struct Config { api_key: String, } impl Config { fn from_cli_args(args: CliArgs) -> Self { Self { api_key: args.api_key } } } #[tokio::main] async fn main() -> Result<()> { let args = CliArgs::parse(); let config = Config::from_cli_args(args); run(config).await? } ``` **绝不** 使用读取环境的 Default trait。始终使用 `from_cli_args()`。 ## 测试标准 **关键规则**:绝不在测试中使用 `std::env::set_var()`。 **正确模式** - 通过参数传递配置: ```rust pub struct Client { api_key: String, } impl Client { pub fn new(api_key: String) -> Self { Self { api_key } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_client() { // 直接传递测试配置 let client = Client::new("test-key".to_string()); assert_eq!(client.api_key, "test-key"); } } ``` ## 代码质量标准 遵循这些原则: - **可见性**:私有(默认)> pub(crate) > pub - **魔数**:使用 `const` 或 CLI 参数,绝不使用字面量 - **异步运行时**:一致使用 tokio - **破坏性更改**:内部 crate 可以,保留 HTTP/WebSocket API 兼容性 ## 模块组织 **通过拆分大型模块保持文件专注和可管理。** ### 何时拆分模块 在以下情况下将模块拆分为子模块: 1. **文件超过约 500 行**并包含语义上不同的组件 2. **测试占文件的 50% 或更多** - 提取到单独的测试模块 3. **存在多个不同的职责**,可以清晰分离 4. **重新导出可以保持公共 API**而不破坏现有代码 ### 测试模块提取 当测试占文件主导地位(约 50%+ 行)时,将它们移动到单独的文件: **之前**(单个文件 `parser.rs`): ```rust // 200 行实现 pub fn parse(input: &str) -> Result { ... } #[cfg(test)] mod tests { // 250 行测试 - 应该提取! } ``` **之后** - 兄弟文件方法(首选): ``` src/ ├── parser.rs # 仅实现 └── parser_tests.rs # 兄弟文件中的测试 ``` ```rust // parser.rs pub fn parse(input: &str) -> Result { ... } #[cfg(test)] #[path = "parser_tests.rs"] mod tests; ``` 将测试文件保存在与源文件相同的目录中(例如 `src/mymodule.rs` → `src/mymodule_tests.rs`)。 **替代** - 子模块方法(当测试需要子目录结构时): ``` src/ ├── parser.rs # 仅实现 └── parser/ └── tests.rs # 子模块中的测试 ``` ```rust // parser.rs #[cfg(test)] #[path = "parser/tests.rs"] mod tests; ``` ### 拆分实现模块 出于语义原因拆分时: ``` // 之前:带有 600+ 行的大型 api.rs src/api.rs // 之后:具有专注子模块的 api/ 目录 src/ └── api/ ├── mod.rs # 重新导出公共项目 ├── client.rs # 客户端实现 ├── endpoints.rs # 端点定义 └── types.rs # 请求/响应类型 ``` **拆分的关键规则:** 1. **保持可见性** - 在 `mod.rs` 中使用 `pub use` 重新导出以保持相同的公共 API 2. **保持相关代码在一起** - 不要拆分紧密耦合的代码 3. **当项目需要跨模块访问但不应公开时使用 `pub(super)` 或 `pub(crate)`** ```rust // api/mod.rs - 重新导出公共接口 mod client; mod endpoints; mod types; pub use client::ApiClient; pub use endpoints::{get_user, create_order}; pub use types::{Request, Response}; ``` ### 何时不拆分 - **少于 300 行** - 通常不值得开销 - **紧密耦合的代码** - 拆分会需要过多的 `pub` 可见性 - **单一职责** - 文件专注,即使很长(例如,复杂算法) - **人为边界** - 如果它强制属于一起的实体之间不自然的分离,不要拆分 - **外部化内部** - 如果拆分需要使私有项目 `pub(crate)` 或 `pub` 仅为跨模块访问,耦合表明它们应该保持在一起 ## 资源 ### scripts/ - `check_crate_version.py`:查询 crates.io 以获取最新依赖项版本 ### references/ - `standards.md`:完整的 Rust 项目标准和规则 - `common-patterns.md`:常见 Rust 模式和挑战的解决方案 ### assets/ - `workspace-template/`:遵循所有标准的样板工作空间结构 ## 工作流摘要 在处理 Rust 代码时: 1. **检查版本**:确保所有 Cargo.toml 文件中的 `edition = "2024"` 2. **添加依赖项**:使用 `check_crate_version.py` 查找最新版本,使用 `x.x` 格式添加 3. **处理错误**:始终使用 `?` 传播错误,绝不使用日志记录或 unwrap_or 吞没 4. **使用正确的错误类型**:库使用 thiserror,二进制使用 anyhow 5. **遵循 CLI-first 配置**:绝不绕过 CLI 参数解析 6. **无环境污染的测试**:通过参数传递配置,绝不使用 env::set_var 7. **参考模式**:检查 `common-patterns.md` 获取惯用解决方案 8. **使用工作空间模板**:对于新项目,从提供的模板开始 9. **组织模块**:拆分 >500 行的文件;当测试占文件 50%+ 时,将测试提取到兄弟 `_tests.rs` 文件