Rust Patterns

affaan-m/ECC/docs/ja-JP/skills/rust-patterns

by affaan-mef648e01899ba3e8dc6371642deaaf64b4477775No license275K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 4 days ago

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

Instructions onlySoftware Development
AI-generated overview

Guidance on idiomatic Rust patterns: ownership, error handling, traits, concurrency and best practices.

What it does
This skill supplies reference guidance on idiomatic Rust development, covering ownership and borrowing, error handling with Result and the question-mark operator, enums and pattern matching, traits and generics, data modelling, iterators, concurrency, unsafe code rules, and module and crate layout. It presents good and bad code examples, a quick-reference idiom table, and anti-patterns to avoid. It also lists common Cargo commands for building, testing, auditing and benchmarking. It is instruction-only and produces no files or scripts.
When to use it
Use it when writing new Rust code, reviewing or refactoring existing Rust code, or designing crate structure and module layout. It suits situations where idiomatic conventions and safety practices should be applied or checked.
Requirements
No tools, packages or credentials beyond the agent; it is instructions only and ships no scripts. The examples reference crates such as thiserror, anyhow, tokio and reqwest, and Cargo commands, but these are illustrative rather than required to read the skill.

Rust 開発パターン

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

使用場面

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

動作原理

このスキルは6つの重要な領域で慣用的なRustの規約を強制する:コンパイル時のデータ競合防止のための所有権と借用、ライブラリではthiserror、アプリケーションではanyhowを使用したResult/?エラー伝播、不正な状態を表現不可能にする列挙型と完全パターンマッチング、ゼロコスト抽象化のためのトレイトとジェネリクス、Arc<Mutex<T>>、チャンネル、async/awaitによる安全な並行処理、ドメインで整理された最小化されたpubインターフェース。

コア原則

1. 所有権と借用

Rustの所有権システムはコンパイル時にデータ競合とメモリエラーを防ぐ。

rust
// Good: Pass references when you don't need ownershipfn process(data: &[u8]) -> usize {    data.len()}
// Good: Take ownership only when you need to store or consumefn store(data: Vec<u8>) -> Record {    Record { payload: data }}
// Bad: Cloning unnecessarily to avoid borrow checkerfn process_bad(data: &Vec<u8>) -> usize {    let cloned = data.clone(); // Wasteful — just borrow    cloned.len()}

柔軟な所有権のための Cow の使用

rust
use std::borrow::Cow;
fn normalize(input: &str) -> Cow<'_, str> {    if input.contains(' ') {        Cow::Owned(input.replace(' ', "_"))    } else {        Cow::Borrowed(input) // Zero-cost when no mutation needed    }}

エラー処理

Result と ? を使用する——本番環境では unwrap() を絶対に使わない

rust
// Good: Propagate errors with contextuse 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)}
// Bad: Panics on errorfn load_config_bad(path: &str) -> Config {    let content = std::fs::read_to_string(path).unwrap(); // Panics!    toml::from_str(&content).unwrap()}

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

rust
// Library code: structured, typed errorsuse 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),}
// Application code: flexible error handlinguse anyhow::{bail, Result};
fn run() -> Result<()> {    let config = load_config("app.toml")?;    if config.workers == 0 {        bail!("worker count must be > 0");    }    Ok(())}

ネストしたマッチの代わりに Option コンビネーターを優先する

rust
// Good: Combinator chainfn find_user_email(users: &[User], id: u64) -> Option<String> {    users.iter()        .find(|u| u.id == id)        .map(|u| u.email.clone())}
// Bad: Deeply nested matchingfn 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,    }}

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

状態を列挙型としてモデル化する

rust
// Good: Impossible states are unrepresentableenum 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),    }}

完全マッチング——ビジネスロジックではワイルドカードを使わない

rust
// Good: Handle every variant explicitlymatch command {    Command::Start => start_service(),    Command::Stop => stop_service(),    Command::Restart => restart_service(),    // Adding a new variant forces handling here}
// Bad: Wildcard hides new variantsmatch command {    Command::Start => start_service(),    _ => {} // Silently ignores Stop, Restart, and future variants}

トレイトとジェネリクス

ジェネリックを受け取り、具体的な型を返す

rust
// Good: Generic input, concrete outputfn read_all(reader: &mut impl Read) -> std::io::Result<Vec<u8>> {    let mut buf = Vec::new();    reader.read_to_end(&mut buf)?;    Ok(buf)}
// Good: Trait bounds for multiple constraintsfn process<T: Display + Send + 'static>(item: T) -> String {    format!("processed: {item}")}

動的ディスパッチにトレイトオブジェクトを使用する

rust
// Use when you need heterogeneous collections or plugin systemstrait Handler: Send + Sync {    fn handle(&self, request: &Request) -> Response;}
struct Router {    handlers: Vec<Box<dyn Handler>>,}
// Use generics when you need performance (monomorphization)fn fast_process<H: Handler>(handler: &H, request: &Request) -> Response {    handler.handle(request)}

型安全のためにNewTypeパターンを使用する

rust
// Good: Distinct types prevent mixing up argumentsstruct UserId(u64);struct OrderId(u64);
fn get_order(user: UserId, order: OrderId) -> Result<Order> {    // Can't accidentally swap user and order IDs    todo!()}
// Bad: Easy to swap argumentsfn get_order_bad(user_id: u64, order_id: u64) -> Result<Order> {    todo!()}

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

複雑な構築にはビルダーパターンを使用する

rust
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 }    }}
// Usage: ServerConfig::builder("localhost", 8080).max_connections(200).build()

イテレーターとクロージャー

手動ループの代わりにイテレーターチェーンを優先する

rust
// Good: Declarative, lazy, composablelet active_emails: Vec<String> = users.iter()    .filter(|u| u.is_active)    .map(|u| u.email.clone())    .collect();
// Bad: Imperative accumulationlet mut active_emails = Vec::new();for user in &users {    if user.is_active {        active_emails.push(user.email.clone());    }}

型注釈付きの collect() を使用する

rust
// Collect into different typeslet 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();
// Collect Results — short-circuits on first errorlet parsed: Result<Vec<i32>, _> = strings.iter().map(|s| s.parse()).collect();

並行処理

共有可変状態には Arc<Mutex<T>> を使用する

rust
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");}

メッセージパッシングにチャンネルを使用する

rust
use std::sync::mpsc;
let (tx, rx) = mpsc::sync_channel(16); // Bounded channel with backpressure
for i in 0..5 {    let tx = tx.clone();    std::thread::spawn(move || {        tx.send(format!("message {i}")).expect("receiver disconnected");    });}drop(tx); // Close sender so rx iterator terminates
for msg in rx {    println!("{msg}");}

Tokioを使用した非同期プログラミング

rust
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")}
// Spawn concurrent tasksasync 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 を使用できる場合

rust
// Acceptable: FFI boundary with documented invariants (Rust 2024+)/// # Safety/// `ptr` must be a valid, aligned pointer to an initialized `Widget`.unsafe fn widget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {    // SAFETY: caller guarantees ptr is valid and aligned    unsafe { &*ptr }}
// Acceptable: Performance-critical path with proof of correctness// SAFETY: index is always < len due to the loop boundunsafe { slice.get_unchecked(index) }

Unsafe を使用してはいけない場合

rust
// Bad: Using unsafe to bypass borrow checker// Bad: Using unsafe for convenience// Bad: Using unsafe without a Safety comment// Bad: Transmuting between unrelated types

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

型ではなくドメインで整理する

text
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

可視性——露出を最小化する

rust
// Good: pub(crate) for internal sharingpub(crate) fn validate_input(input: &str) -> bool {    !input.is_empty()}
// Good: Re-export public API from lib.rspub mod auth;pub use auth::AuthMiddleware;
// Bad: Making everything pubpub fn internal_helper() {} // Should be pub(crate) or private

ツール統合

基本コマンド

bash
# Build and checkcargo buildcargo check              # Fast type checking without codegencargo clippy             # Lints and suggestionscargo fmt                # Format code
# Testingcargo testcargo test -- --nocapture    # Show println outputcargo test --lib             # Unit tests onlycargo test --test integration # Integration tests only
# Dependenciescargo audit              # Security auditcargo tree               # Dependency treecargo update             # Update dependencies
# Performancecargo bench              # Run benchmarks

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

イディオム説明
借用、クローンではなく所有権が必要でなければ &T を渡し、クローンしない
不正な状態を表現不可能にする列挙型を使用して有効な状態のみをモデル化する
unwrap() より ?エラーを伝播し、ライブラリ/本番コードでパニックしない
検証より解析境界で非構造化データを型付き構造体に変換する
型安全のためのNewtype基本型をnewtypeでラップして引数の取り違えを防ぐ
ループよりイテレーターを優先宣言的なチェーンはより明確で通常より高速
Resultに #[must_use] を使用呼び出し元が戻り値を処理することを保証する
柔軟な所有権のために Cow を使用借用で十分な場合にアロケーションを避ける
完全マッチングビジネスクリティカルな列挙型でワイルドカード _ を使わない
pub インターフェースを最小化内部APIには pub(crate) を使用

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

rust
// Bad: .unwrap() in production codelet value = map.get("key").unwrap();
// Bad: .clone() to satisfy borrow checker without understanding whylet data = expensive_data.clone();process(&original, &data);
// Bad: Using String when &str sufficesfn greet(name: String) { /* should be &str */ }
// Bad: Box<dyn Error> in libraries (use thiserror instead)fn parse(input: &str) -> Result<Data, Box<dyn std::error::Error>> { todo!() }
// Bad: Ignoring must_use warningslet _ = validate(input); // Silently discarding a Result
// Bad: Blocking in async contextasync fn bad_async() {    std::thread::sleep(Duration::from_secs(1)); // Blocks the executor!    // Use: tokio::time::sleep(Duration::from_secs(1)).await;}

覚えておくこと:コンパイルが通れば、おそらく正しい——ただし unwrap() を避け、unsafe を最小化し、型システムを活用することが前提。

Source and attribution

Source:affaan-m/ECCindocs/ja-JP/skills/rust-patternsat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal