Communitygithub.com

Rust 開発パターン

所有権、エラー処理、トレイト、並行処理など、Rustで安全かつ高性能なアプリケーションを構築するための慣用的なパターンとベストプラクティス。

Rust 開発パターン とは?

このスキルは、Rustコードの記述、レビュー、リファクタリングにおいて、所有権・借用、エラー処理、トレイト、並行処理など6つの主要分野にわたる慣用的なパターンを提供します。コンパイル時にデータ競合を防ぐ所有権システム、`thiserror`と`anyhow`を用いたエラー伝搬、列挙型による到達不可能な状態の排除、ゼロコスト抽象化のためのトレイトとジェネリクス、`Arc<Mutex<T>>`やチャネル、async/awaitによる安全な並行処理を重視します。さらに、ドメイン別のモジュール構成や最小限の`pub`公開範囲、`Cow`による柔軟な所有権、イテレータチェーン、Unsafeコードの適切な使用法などをカバーし、Claude Code、Coder、GitHub Copilot、Codeium、Aiderなどの主要なAI開発ツールで利用可能です。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/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)
 }

型安全性のためのNewtypeパターン

// 良い例: 異なる型が引数の混同を防ぐ
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();

// Resultsを収集 — 最初のエラーで短絡
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を使ったAsync

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
}

Unsafeコード

Unsafeが許容される場合

// 許容: 文書化された不変条件を持つ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を使用
// 悪い例: 便利さのために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()より?エラーを伝搬し、ライブラリ/本番コードでは決してパニックしない
検証するよりパースする境界で非構造化データを型付き構造体に変換する
型安全性のためのNewtypeプリミティブをnewtypeでラップして引数の入れ替えを防ぐ
ループよりイテレータを優先宣言的なチェーンはより明確で、多くの場合高速
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コンテキストでのブロッキング
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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

関連スキル