mcpField
R
rust-best-practices

rust-best-practices

github.com/majiayu000開発29089 回インストール3 日前に更新
リポジトリ

rust-best-practices について

Microsoft Pragmatic Rust GuidelinesとRustコミュニティの基準に基づく、Rust開発者向けの実践ガイドです。型システム、エラー処理、API設計を扱います。

主な機能

  1. 01型システムと所有権の活用
  2. 02明示的なエラー処理の指針
  3. 03ライブラリとアプリのエラー処理
  4. 04BuilderとNewtypeによるAPI設計
  5. 05イテレーター、ジェネリクス、トレイトの活用

活用シーン

  1. 01RustライブラリのAPI設計
  2. 02アプリのエラー処理方法の選択
  3. 03コードのパニックリスク確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/majiayu000/spellbook --skill rust-best-practices

インストール後、Claude Code で/rust-best-practicesを入力して読み込まれたか確認します。

同梱ファイルreferences/advanced.md

詳細

Rust のベストプラクティス

Microsoft Pragmatic Rust Guidelines と Rust コミュニティの標準に基づいています。

基本原則

  1. 型システムを活用する — 無効な状態を表現できないように型を使用します
  2. 所有権を活用する — 借用チェッカーに逆らわず、うまく連携します
  3. 暗黙より明示を優先する — 失敗の可能性、可変性、ライフタイムを明確にします
  4. ゼロコスト抽象化 — 実行時コストなしでイテレーター、ジェネリクス、トレイトを使用します
  5. 速やかに失敗し、適切に復旧する — 早い段階で検証し、エラーを明示的に処理します

エラー処理

ライブラリでは thiserror を使用する
use thiserror::Error;

#[derive(Error, Debug)]
pub enum MyError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    #[error("Parse error at line {line}: {message}")]
    Parse { line: usize, message: String },
    #[error("Not found: {0}")]
    NotFound(String),
}
アプリケーションでは anyhow を使用する
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = load_config()
        .context("Failed to load configuration")?;
    run_app(config)?;
    Ok(())
}
ライブラリではパニックを起こさない
// ❌ BAD
pub fn get_item(index: usize) -> &Item {
    &self.items[index]  // Panics on out-of-bounds
}

// ✅ GOOD
pub fn get_item(&self, index: usize) -> Option<&Item> {
    self.items.get(index)
}

// ✅ GOOD - when you need Result
pub fn get_item(&self, index: usize) -> Result<&Item, Error> {
    self.items.get(index).ok_or(Error::NotFound(index))
}

API 設計

複雑な設定にはビルダーパターンを使用する
pub struct Client {
    url: String,
    timeout: Duration,
    retries: u32,
}

impl Client {
    pub fn builder(url: impl Into<String>) -> ClientBuilder {
        ClientBuilder {
            url: url.into(),
            timeout: Duration::from_secs(30),
            retries: 3,
        }
    }
}

pub struct ClientBuilder {
    url: String,
    timeout: Duration,
    retries: u32,
}

impl ClientBuilder {
    pub fn timeout(mut self, timeout: Duration) -> Self {
        self.timeout = timeout;
        self
    }

    pub fn retries(mut self, retries: u32) -> Self {
        self.retries = retries;
        self
    }

    pub fn build(self) -> Client {
        Client {
            url: self.url,
            timeout: self.timeout,
            retries: self.retries,
        }
    }
}
Newtype パターンを使用する
// ❌ BAD - primitive obsession
fn create_user(name: String, email: String, age: u32) -> User { ... }

// ✅ GOOD - newtype wrappers
pub struct Username(String);
pub struct Email(String);
pub struct Age(u32);

impl Email {
    pub fn new(email: impl Into<String>) -> Result<Self, ValidationError> {
        let email = email.into();
        if email.contains('@') {
            Ok(Self(email))
        } else {
            Err(ValidationError::InvalidEmail)
        }
    }
}

fn create_user(name: Username, email: Email, age: Age) -> User { ... }
柔軟性を高めるため impl Trait を受け入れる
// ❌ BAD - overly specific
pub fn process(items: Vec<String>) { ... }

// ✅ GOOD - accept any iterable
pub fn process(items: impl IntoIterator<Item = impl AsRef<str>>) {
    for item in items {
        println!("{}", item.as_ref());
    }
}

参考資料

コメント