跳转至

Rust 编码风格

此文件扩展 common/coding-style.md,并添加 Rust 特定内容。

格式化

  • 强制使用 rustfmt — 提交前务必运行 cargo fmt
  • 使用 clippy 进行 lint — cargo clippy -- -D warnings(将警告视为错误)
  • 4 空格缩进(rustfmt 默认)
  • 最大行宽:100 字符(rustfmt 默认)

不可变性

Rust 中的变量默认是不可变的 — 请利用这一点:

  • 默认使用 let。仅在需要可变性时使用 let mut
  • 优先返回新值,而不是就地修改
  • 如果不确定函数是否需要分配,请使用 Cow<'_, T>
use std::borrow::Cow;

// 良い例 — デフォルトで不変、新しい値を返す
fn normalize(input: &str) -> Cow<'_, str> {
    if input.contains(' ') {
        Cow::Owned(input.replace(' ', "_"))
    } else {
        Cow::Borrowed(input)
    }
}

// 悪い例 — 不要なミューテーション
fn normalize_bad(input: &mut String) {
    *input = input.replace(' ', "_");
}

命名

遵循标准 Rust 约定: - 函数、方法、变量、模块、crate 使用 snake_case - 类型、trait、枚举、类型参数使用 PascalCase(UpperCamelCase) - 常量和静态变量使用 SCREAMING_SNAKE_CASE - 生命周期:短小写('a、'de)— 复杂情况下使用描述性名称('input)

所有权与借用

  • 默认使用借用(&T)。仅在需要存储或消费时获取所有权
  • 不要在不理解根本原因的情况下,仅为满足借用检查器而克隆
  • 函数参数中,优先接受 &str 而不是 String,&[T] 而不是 Vec<T>
  • 对于需要拥有 String 的构造函数,使用 impl Into<String>
// 良い例 — 所有権が不要な場合は借用する
fn word_count(text: &str) -> usize {
    text.split_whitespace().count()
}

// 良い例 — Into を使用してコンストラクタで所有権を取得する
fn new(name: impl Into<String>) -> Self {
    Self { name: name.into() }
}

// 悪い例 — &str で十分なのに String を取得する
fn word_count_bad(text: String) -> usize {
    text.split_whitespace().count()
}

错误处理

  • 使用 Result<T, E> 和 ? 进行传播 — 生产代码中不要使用 unwrap()
  • 库:使用 thiserror 定义类型化错误
  • 应用程序:使用 anyhow 处理灵活错误上下文
  • 使用 .with_context(|| format!("failed to ..."))? 添加上下文
  • 仅在测试和真正不可达的状态中使用 unwrap() / expect()
// 良い例 — thiserror によるライブラリエラー
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
    #[error("failed to read config: {0}")]
    Io(#[from] std::io::Error),
    #[error("invalid config format: {0}")]
    Parse(String),
}

// 良い例 — anyhow によるアプリケーションエラー
use anyhow::Context;

fn load_config(path: &str) -> anyhow::Result<Config> {
    let content = std::fs::read_to_string(path)
        .with_context(|| format!("failed to read {path}"))?;
    toml::from_str(&content)
        .with_context(|| format!("failed to parse {path}"))
}

优先使用迭代器而非循环

转换优先使用迭代器链。复杂控制流使用循环:

// 良い例 — 宣言的で合成可能
let active_emails: Vec<&str> = users.iter()
    .filter(|u| u.is_active)
    .map(|u| u.email.as_str())
    .collect();

// 良い例 — 早期リターンを伴う複雑なロジックにはループ
for user in &users {
    if let Some(verified) = verify_email(&user.email)? {
        send_welcome(&verified)?;
    }
}

模块结构

按领域而不是按类型组织:

src/
├── main.rs
├── lib.rs
├── auth/           # ドメインモジュール
│   ├── mod.rs
│   ├── token.rs
│   └── middleware.rs
├── orders/         # ドメインモジュール
│   ├── mod.rs
│   ├── model.rs
│   └── service.rs
└── db/             # インフラストラクチャ
    ├── mod.rs
    └── pool.rs

可见性

  • 默认是私有的。内部共享使用 pub(crate)
  • 仅对 crate 公共 API 的一部分使用 pub
  • 从 lib.rs 重新导出公共 API

参考

有关全面的 Rust 惯用法和模式,请参见技能:rust-patterns。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC