Communitygithub.com

rust-patterns

慣用的 Rust 模式、所有權、錯誤處理、特徵、並行處理以及建構安全高效能應用程式的最佳實踐。在撰寫或審查 Rust 程式碼,且涉及所有權、錯誤處理、特徵或並行處理等問題時使用。

rust-patterns 是什麼?

此技能針對開發人員在撰寫、審查或重構 Rust 程式碼時,提供一套強制遵循慣用 Rust 慣例的全面指南。它涵蓋六大關鍵領域:所有權與借用(於編譯期防止資料競爭)、使用 `thiserror` 和 `anyhow` 的 `Result`/`?` 錯誤傳播、透過列舉和窮盡模式匹配使非法狀態無法被表示、利用特徵與泛型實現零成本抽象、透過 `Arc<Mutex<T>>`、通道及 async/await 實現安全並行,以及以領域為導向組織最小化公開 (`pub`) 介面。此技能支援所有 Rust 專案,透過清晰的程式碼範例、反模式警示以及包含建構、測試、依賴管理和效能驗證等指令的工具整合章節,幫助開發人員充分利用 Rust 的類型系統和所有權模型,建構安全、高效能且易於維護的應用程式。這能確保程式碼不僅能編譯成功,且更具表達力、彈性且不易出錯。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/rust-patterns

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Rust 開發模式

用於建構安全、高效能且易於維護的應用程式的慣用 Rust 模式和最佳實踐。

使用時機

  • 撰寫新的 Rust 程式碼
  • 審查 Rust 程式碼
  • 重構現有的 Rust 程式碼
  • 設計 crate 結構與模組佈局

運作方式

此技能在六個關鍵領域強制執行慣用的 Rust 慣例:所有權與借用(於編譯期防止資料競爭)、使用 thiserror(函式庫)和 anyhow(應用程式)的 Result/? 錯誤傳播、透過列舉和窮盡模式匹配使非法狀態無法被表示、利用特徵與泛型實現零成本抽象、透過 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!("從 {path} 讀取設定檔失敗"))?;
    let config: Config = toml::from_str(&content)
        .with_context(|| format!("從 {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("找不到記錄:{id}")]
    NotFound { id: String },
    #[error("連線失敗")]
    Connection(#[from] std::io::Error),
    #[error("無效資料:{0}")]
    InvalidData(String),
}

// 應用程式程式碼:靈活的錯誤處理
use anyhow::{bail, Result};

fn run() -> Result<()> {
    let config = load_config("app.toml")?;
    if config.workers == 0 {
        bail!("工作者數量必須 > 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!("已處理:{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();

// 收集 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 已中毒");
        *num += 1;
    })
}).collect();

for handle in handles {
    handle.join().expect("工作執行緒已崩潰");
}

使用通道傳遞訊息

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!("訊息 {i}")).expect("接收端已斷開連線");
    });
}
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("請求逾時")?
    .context("請求失敗")?;

    response.text().await.context("讀取回應主體失敗")
}

// 產生並發任務
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!("產生的任務已崩潰:{e}")));
    }
    results
}

不安全的程式碼

何時可以使用 unsafe

// 可接受:具有記錄不變量的 FFI 邊界(Rust 2024+)
/// # 安全性
/// `ptr` 必須是已初始化 `Widget` 的有效、對齊的指標。
unsafe fn widget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {
    // 安全性:呼叫者保證 ptr 是有效且對齊的
    unsafe { &*ptr }
}

// 可接受:具有正確性證明的效能關鍵路徑
// 安全性:由於迴圈邊界,index 總是 < len
unsafe { slice.get_unchecked(index) }

何時不應使用 unsafe

// 壞:使用 unsafe 繞過借用檢查器
// 壞:為了方便而使用 unsafe
// 壞:使用 unsafe 但沒有安全性註解
// 壞:在不同型別之間進行 transmute

模組系統與 Crate 結構

按領域組織,而非按型別

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()傳播錯誤,在函式庫/生產環境中絕不崩潰
解析,不要驗證在邊界處將非結構化資料轉換為型別結構體
使用新型別確保型別安全將基本型別包裝在新型別中以防止參數交換
優先使用迭代器而非迴圈宣告式鏈更清晰且通常更快
為 Results 加上 #[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/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.

相關技能