Rust 开发指南
引言
OneArchive 后端采用 Rust 语言开发,以获得高性能、内存安全和并发优势。本指南提供 Rust 开发环境配置、常用工具使用,以及项目遵循的代码规范和最佳实践。
1. 环境配置
1.1. 安装 Rust
使用 rustup(Rust 官方工具链管理器)安装:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh安装完成后,确保 Rust 添加到 PATH 环境变量。
1.2. 更新工具链
保持 Rust 工具链为最新稳定版本:
rustup update stable
rustup default stable1.3. 安装内置组件
安装 Rust 官方格式化工具和 Lint 工具:
rustup component add rustfmt
rustup component add clippy2. 常用 Cargo 工具
推荐安装以下 Cargo 工具,提升开发体验:
cargo install cargo-edit # cargo add/remove/update
cargo install cargo-watch # 文件变动自动 check/test
cargo install cargo-expand # 展开宏
cargo install cargo-outdated # 检查过时依赖
cargo install cargo-audit # 检查安全漏洞
cargo install cargo-udeps --locked # 检查未使用依赖
cargo install cargo-tarpaulin # 代码覆盖率3. 工具使用场景
3.1. 日常开发
- 代码格式化:使用
cargo fmt确保代码风格一致。 - 静态分析:使用
cargo clippy发现潜在问题和改进点。 - 依赖管理:使用
cargo-edit(cargo add/remove/update)简化依赖管理。 - 开发监控:使用
cargo watch -x check或cargo watch -x test在文件变更时自动检查或测试。
3.2. 调试与分析
- 宏展开:使用
cargo expand查看宏展开后的代码,便于调试复杂的宏。 - 依赖检查:使用
cargo outdated检查过时依赖,cargo udeps检查未使用依赖。 - 安全审计:使用
cargo audit检查依赖中的已知安全漏洞。
3.3. 测试与覆盖率
代码覆盖率:使用
cargo tarpaulin生成代码覆盖率报告:bashcargo tarpaulin --out Html生成 HTML 格式覆盖率报告,位于
tarpaulin-report.html。
4. 代码规范
4.1. 模块组织
采用 Rust 2018+ 模块系统,不再使用 mod.rs。
模块 foo 定义在 src/foo.rs,其子模块 foo::bar 定义在 src/foo/bar.rs。
避免将所有逻辑塞入单个 .rs 文件;文件过大时,拆分为多个逻辑子模块。
4.2. 子模块命名规范
所有子模块文件使用统一前缀格式:
api_: 接口定义。impl_: 具体实现代码(结构体 impl、函数实现)。trait_: Trait 定义。model_: 数据模型、结构体、枚举定义。utils_: 工具函数、辅助代码。schema_: 数据库 schema 定义。constants_: 常量定义。common_: 通用代码、共享逻辑。dao_: DAO 层实现。core_: 核心逻辑。service_: 服务层实现。
通用顶级模块使用 api,非通用顶级模块保持 mod_ 前缀,子模块统一使用上述前缀。目录下文件遵循相同规则,避免无前缀或混合命名。
4.3. 测试文件命名规范
测试文件(位于 tests/ 目录下的集成测试)统一使用 test_ 前缀,如 test_archive.rs、test_database.rs。
这是 Rust 标准约定,Cargo 自动识别并运行,无需额外配置。测试代码中的模块引用与源码一致,确保路径更新同步。
4.4. 函数与结构
- 单个函数体(不含注释和空行)不应超过 50 行。理想长度 25 行以内。
- 公共逻辑提取到独立模块(如
utils.rs、error.rs)。 - 优先使用
Result和Option进行错误和空值处理,避免 panic。 - 结构体和枚举合理拆分,避免"上帝对象"。
- 可以添加 log debug、info 等辅助排查问题,或修改错误暴露方式以排查 bug。
4.5. 代码风格
遵循 Rust 官方代码风格指南。
使用 cargo fmt 作为格式化标准。
避免使用 unwrap() 和 expect(),使用适当错误处理。
所有公开 API 必须有文档注释(///)。
避免不必要的 clone(),优先使用引用或 Cow。
注释重要,但不要大篇幅添加注释。
5. 错误处理
使用 thiserror crate 定义自定义错误类型。
避免使用 anyhow 进行泛化错误处理,除非在顶层入口点。
6. 数据库抽象层实现
6.1. 统一错误类型
#[derive(Debug)]
pub enum DatabaseError {
Rusqlite(rusqlite::Error),
#[cfg(feature = "sqlx")]
Sqlx(sqlx::Error),
// ... 其他数据库错误
Custom(String),
}6.2. Repository Trait
pub trait ArchiveRepository {
fn find_by_id(&self, id: i64) -> Result<Option<Archive>, DatabaseError>;
fn save(&self, archive: &Archive) -> Result<(), DatabaseError>;
}6.3. 具体实现
pub struct RusqliteArchiveRepository {
conn: Arc<Connection>,
}
impl ArchiveRepository for RusqliteArchiveRepository {
// ... 实现
}6.4. 实现与组织
- 接口定义:在
src/db/目录下定义所有 trait。 - 具体实现:为每种数据库创建独立的模块,如
src/db/rusqlite_impl/。 - 依赖注入:服务层通过泛型参数接收 Repository 实例,优先使用泛型以获得更好性能。
6.5. 禁止事项
- 禁止在业务逻辑中直接使用具体数据库 API。
- 禁止在 trait 方法签名中使用具体数据库类型。
- 禁止在没有 feature gate 的情况下引入可选依赖。