Communitygithub.com

Rust開発パターン

イディオマティックなRustパターン、所有権、エラー処理、トレイト、並行処理、そして安全で高性能なアプリケーションを構築するためのベストプラクティス。Rustコードの作成やレビュー時、所有権やエラー処理、トレイト、並行性に関する問題がある場合に使用します。

Rust開発パターン とは?

このスキルは、安全で高性能かつ保守性の高いRustアプリケーションを構築するための、イディオマティックなRustパターンとベストプラクティスを提供します。所有権と借用(コンパイル時のデータ競合防止)、`Result`/`?`によるエラー伝搬(ライブラリでは`thiserror`、アプリケーションでは`anyhow`を使用)、列挙型と網羅的パターンマッチング(不正な状態を表現不可能にする)、トレイトとジェネリクス(ゼロコスト抽象化)、`Arc<Mutex<T>>`やチャネル、async/awaitを用いた安全な並行処理、そして最小限の`pub`公開範囲といった6つの主要分野をカバーします。このスキルは主にコード補完ツール(Claude Code、Coder、Codexなど)向けに設計されており、開発者がコンパイルチェックを信頼しながら安全で表現力豊かなコードを記述できるように導きます。さらに、`unwrap()`を避け、`unsafe`を最小限に抑え、型システムを最大限に活用するための具体的なコードパターンとアンチパターンを提供します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/rust-patterns

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Rust開発パターン は何をしますか?

安全で高性能かつ保守性の高いアプリケーションを構築するためのイディオマティックなRustパターンとベストプラクティス。

使用すべき場面

  • 新しいRustコードを書くとき
  • Rustコードをレビューするとき
  • 既存のRustコードをリファクタリングするとき
  • クレート構造とモジュールレイアウトを設計するとき

動作の仕組み

このスキルは、6つの主要分野にわたってイディオマティックなRustの慣習を強制します。所有権と借用(コンパイル時のデータ競合防止)、Result/?によるエラー伝搬(ライブラリではthiserror、アプリケーションではanyhowを使用)、列挙型と網羅的パターンマッチング(不正な状態を表現不可能にする)、トレイトとジェネリクス(ゼロコスト抽象化)、Arc<Mutex<T>>、チャネル、async/awaitによる安全な並行処理、そしてドメインごとに整理された最小限のpub公開範囲です。

核となる原則

1. 所有権と借用

Rustの所有権システムは、コンパイル時にデータ競合やメモリバグを防止します。

// 良い: 所有権が不要な場合は参照を渡す
fn process(data: &[u8]) -> usize {
    data.len()
}

// 良い: 値を保存または消費する必要がある場合のみ所有権を取る
fn store(data: Vec<u8>) -> Record {
    Record { payload: data }
}

// 悪い: 借用チェッカーを回避するために不必要にクローンする
fn process_bad(data: &Vec<u8>) -> usize {
    let cloned = data.clone(); // 無駄 — 借用するだけでよい
    cloned.len()
}

柔軟な所有権のためにCowを使う

use std::borrow::Cow;

fn normalize(input: &str) -> Cow<'_, str> {
    if input.contains(' ') {
        Cow::Owned(input.replace(' ', "_"))
    } else {
        Cow::Borrowed(input) // 変更が不要な場合はゼロコスト
    }
}

エラー処理

Resultと?を使い、本番コードでunwrap()は決して使わない

// 良い: コンテキスト付きでエラーを伝搬する
use anyhow::{Context, Result};

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

// 悪い: エラーでパニックする
fn load_config_bad(path: &str) -> Config {
    let content = std::fs::read_to_string(path).unwrap(); // パニック!
    toml::from_str(&content).unwrap()
}

ライブラリのエラーはthiserror、アプリケーションのエラーはanyhow

// ライブラリコード: 構造化された型付きエラー
use thiserror::Error;

#[derive(Debug, Error)]
pub enum StorageError {
    #[error("record not found: {id}")]
    NotFound { id: String },
    #[error("connection failed")]
    Connection(#[from] std::io::Error),
    #[error("invalid data: {0}")]
    InvalidData(String),
}

// アプリケーションコード: 柔軟なエラー処理
use anyhow::{bail, Result};

fn run() -> Result<()> {
    let config = load_config("app.toml")?;
    if config.workers == 0 {
        bail!("worker count must be > 0");
    }
    Ok(())
}

ネストしたマッチングよりもOptionのコンビネータ

// 良い: コンビネータチェーン
fn find_user_email(users: &[User], id: u64) -> Option<String> {
    users.iter()
        .find(|u| u.id == id)
        .map(|u| u.email.clone())
}

// 悪い: 深くネストしたマッチング
fn find_user_email_bad(users: &[User], id: u64) -> Option<String> {
    match users.iter().find(|u| u.id == id) {
        Some(user) => match &user.email {
            email => Some(email.clone()),
        },
        None => None,
    }
}

列挙型とパターンマッチング

状態を列挙型でモデル化する

// 良い: 不正な状態が表現不可能になる
enum ConnectionState {
    Disconnected,
    Connecting { attempt: u32 },
    Connected { session_id: String },
    Failed { reason: String, retries: u32 },
}

fn handle(state: &ConnectionState) {
    match state {
        ConnectionState::Disconnected => connect(),
        ConnectionState::Connecting { attempt } if *attempt > 3 => abort(),
        ConnectionState::Connecting { .. } => wait(),
        ConnectionState::Connected { session_id } => use_session(session_id),
        ConnectionState::Failed { retries, .. } if *retries < 5 => retry(),
        ConnectionState::Failed { reason, .. } => log_failure(reason),
    }
}

ビジネスロジックでは網羅的マッチング — 包括ケースは使わない

// 良い: すべてのバリアントを明示的に処理する
match command {
    Command::Start => start_service(),
    Command::Stop => stop_service(),
    Command::Restart => restart_service(),
    // 新しいバリアントを追加するとここでの処理が強制される
}

// 悪い: ワイルドカードが新しいバリアントを隠す
match command {
    Command::Start => start_service(),
    _ => {} // Stop、Restart、および将来のバリアントを黙って無視する
}

トレイトとジェネリクス

入力はジェネリックに、戻り値は具象型に

// 良い: ジェネリックな入力、具象型の出力
fn read_all(reader: &mut impl Read) -> std::io::Result<Vec<u8>> {
    let mut buf = Vec::new();
    reader.read_to_end(&mut buf)?;
    Ok(buf)
}

// 良い: 複数の制約に対するトレイト境界
fn process<T: Display + Send + 'static>(item: T) -> String {
    format!("processed: {item}")
}

動的ディスパッチのためのトレイトオブジェクト

// 異種コレクションやプラグインシステムが必要な場合に使用する
trait Handler: Send + Sync {
    fn handle(&self, request: &Request) -> Response;
}

struct Router {
    handlers: Vec<Box<dyn Handler>>,
}

// パフォーマンスが必要な場合はジェネリクスを使用する(単相化)
fn fast_process<H: Handler>(handler: &H, request: &Request) -> Response {
    handler.handle(request)
}

型安全性のためのニュータイプパターン

// 良い: 異なる型で引数の混同を防ぐ
struct UserId(u64);
struct OrderId(u64);

fn get_order(user: UserId, order: OrderId) -> Result<Order> {
    // ユーザーIDと注文IDを誤って入れ替えることはできない
    todo!()
}

// 悪い: 引数を入れ替えやすい
fn get_order_bad(user_id: u64, order_id: u64) -> Result<Order> {
    todo!()
}

構造体とデータモデリング

複雑な構築にはビルダーパターン

struct ServerConfig {
    host: String,
    port: u16,
    max_connections: usize,
}

impl ServerConfig {
    fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
        ServerConfigBuilder { host: host.into(), port, max_connections: 100 }
    }
}

struct ServerConfigBuilder { host: String, port: u16, max_connections: usize }

impl ServerConfigBuilder {
    fn max_connections(mut self, n: usize) -> Self { self.max_connections = n; self }
    fn build(self) -> ServerConfig {
        ServerConfig { host: self.host, port: self.port, max_connections: self.max_connections }
    }
}

// 使用法: ServerConfig::builder("localhost", 8080).max_connections(200).build()

イテレータとクロージャ

手動ループよりもイテレータチェーンを優先する

// 良い: 宣言的、遅延評価、合成可能
let active_emails: Vec<String> = users.iter()
    .filter(|u| u.is_active)
    .map(|u| u.email.clone())
    .collect();

// 悪い: 命令的な累積
let mut active_emails = Vec::new();
for user in &users {
    if user.is_active {
        active_emails.push(user.email.clone());
    }
}

型注釈付きでcollect()を使う

// 異なる型に収集する
let names: Vec<_> = items.iter().map(|i| &i.name).collect();
let lookup: HashMap<_, _> = items.iter().map(|i| (i.id, i)).collect();
let combined: String = parts.iter().copied().collect();

// Resultを収集 — 最初のエラーでショートサーキットする
let parsed: Result<Vec<i32>, _> = strings.iter().map(|s| s.parse()).collect();

並行処理

共有可変状態にはArc<Mutex<T>>

use std::sync::{Arc, Mutex};

let counter = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..10).map(|_| {
    let counter = Arc::clone(&counter);
    std::thread::spawn(move || {
        let mut num = counter.lock().expect("mutex poisoned");
        *num += 1;
    })
}).collect();

for handle in handles {
    handle.join().expect("worker thread panicked");
}

メッセージパッシングにはチャネル

use std::sync::mpsc;

let (tx, rx) = mpsc::sync_channel(16); // バックプレッシャー付きの有界チャネル

for i in 0..5 {
    let tx = tx.clone();
    std::thread::spawn(move || {
        tx.send(format!("message {i}")).expect("receiver disconnected");
    });
}
drop(tx); // 送信側を閉じて、rxのイテレータが終了するようにする

for msg in rx {
    println!("{msg}");
}

Tokioを使った非同期処理

use tokio::time::Duration;

async fn fetch_with_timeout(url: &str) -> Result<String> {
    let response = tokio::time::timeout(
        Duration::from_secs(5),
        reqwest::get(url),
    )
    .await
    .context("request timed out")?
    .context("request failed")?;

    response.text().await.context("failed to read body")
}

// 並行タスクを生成する
async fn fetch_all(urls: Vec<String>) -> Vec<Result<String>> {
    let handles: Vec<_> = urls.into_iter()
        .map(|url| tokio::spawn(async move {
            fetch_with_timeout(&url).await
        }))
        .collect();

    let mut results = Vec::with_capacity(handles.len());
    for handle in handles {
        results.push(handle.await.unwrap_or_else(|e| panic!("spawned task panicked: {e}")));
    }
    results
}

アンセーフコード

アンセーフが許容されるケース

// 許容: ドキュメント化された不変条件を伴うFFI境界 (Rust 2024+)
/// ## Safety
/// `ptr` は初期化された `Widget` への有効かつアラインされたポインタでなければなりません。
unsafe fn widget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {
    // SAFETY: 呼び出し元がptrの有効性とアライメントを保証する
    unsafe { &*ptr }
}

// 許容: 正当性の証明を伴うパフォーマンスクリティカルなパス
// SAFETY: ループの境界によりindexは常にlen未満
unsafe { slice.get_unchecked(index) }

アンセーフが許容されないケース

// 悪い: 借用チェッカーを回避するためにunsafeを使う
// 悪い: 便宜上unsafeを使う
// 悪い: Safetyコメントなしでunsafeを使う
// 悪い: 無関係な型間で transmute する

モジュールシステムとクレート構造

型ごとではなく、ドメインごとに整理する

my_app/
├── src/
│   ├── main.rs
│   ├── lib.rs
│   ├── auth/          # ドメインモジュール
│   │   ├── mod.rs
│   │   ├── token.rs
│   │   └── middleware.rs
│   ├── orders/        # ドメインモジュール
│   │   ├── mod.rs
│   │   ├── model.rs
│   │   └── service.rs
│   └── db/            # インフラストラクチャ
│       ├── mod.rs
│       └── pool.rs
├── tests/             # 統合テスト
├── benches/           # ベンチマーク
└── Cargo.toml

可視性 — 最小限に公開する

// 良い: 内部共有にはpub(crate)
pub(crate) fn validate_input(input: &str) -> bool {
    !input.is_empty()
}

// 良い: lib.rsから公開APIを再エクスポートする
pub mod auth;
pub use auth::AuthMiddleware;

// 悪い: すべてをpubにする
pub fn internal_helper() {} // pub(crate)またはプライベートであるべき

ツール連携

必須コマンド

# ビルドとチェック
cargo build
cargo check              # コード生成なしの高速型チェック
cargo clippy             # リントと提案
cargo fmt                # コードフォーマット

# テスト
cargo test
cargo test -- --nocapture    # printlnの出力を表示
cargo test --lib             # ユニットテストのみ
cargo test --test integration # 統合テストのみ

# 依存関係
cargo audit              # セキュリティ監査
cargo tree               # 依存関係ツリー
cargo update             # 依存関係の更新

# パフォーマンス
cargo bench              # ベンチマークの実行

クイックリファレンス: Rustのイディオム

イディオム説明
クローンせず借用する所有権が必要でない限り、クローンではなく&Tを渡す
不正な状態を表現不可能にする列挙型を使用して有効な状態のみをモデル化する
unwrap() より ?エラーを伝搬し、ライブラリ/本番コードでは決してパニックしない
検証ではなくパースする境界で非構造化データを型付き構造体に変換する
型安全性のためのニュータイププリミティブをニュータイプでラップして引数の入れ替えを防ぐ
ループよりイテレータを優先宣言的なチェーンはより明確で、多くの場合高速
Resultへの#[must_use]呼び出し側に戻り値の処理を強制する
柔軟な所有権にCow借用で十分な場合にアロケーションを回避する
網羅的マッチングビジネスクリティカルな列挙型にはワイルドカード_を使わない
最小限のpub公開範囲内部APIにはpub(crate)を使用する

避けるべきアンチパターン

// 悪い: 本番コードでの.unwrap()
let value = map.get("key").unwrap();

// 悪い: 理由を理解せずに借用チェッカーを満たすための.clone()
let data = expensive_data.clone();
process(&original, &data);

// 悪い: &strで十分な場面でStringを使う
fn greet(name: String) { /* &str であるべき */ }

// 悪い: ライブラリでのBox<dyn Error> (代わりにthiserrorを使う)
fn parse(input: &str) -> Result<Data, Box<dyn std::error::Error>> { todo!() }

// 悪い: must_use警告を無視する
let _ = validate(input); // Resultを黙って破棄している

// 悪い: 非同期コンテキストでのブロッキング
async fn bad_async() {
    std::thread::sleep(Duration::from_secs(1)); // エグゼキュータをブロックする!
    // 代わりに: tokio::time::sleep(Duration::from_secs(1)).await;
}

忘れないでください: コンパイルが通れば、おそらく正しいコードです — ただし、unwrap()を避け、unsafeを最小限に抑え、型システムを活用させることが条件です。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

関連スキル