Communitygithub.com

Rust 开发模式与最佳实践

惯用的 Rust 模式,涵盖所有权、错误处理、泛型、并发及最佳实践,帮助你构建安全、高性能的应用。在编写或审查 Rust 代码,并涉及所有权、错误处理、泛型或并发问题时使用。

Rust 开发模式与最佳实践 是什么?

该技能是一套全面的 Rust 开发指南,专注于七个核心领域:所有权与借用、错误处理 (thiserror/anyhow)、枚举与模式匹配、泛型与特型、并发 (Arc<Mutex<T>>、通道、Tokio)、不安全代码规范以及模块化组织。它强调「让非法状态不可表示」「解析而非验证」「借用而非克隆」等核心理念,并提供了详尽的代码示例和反模式警示。适用于 Cursor、Codex 等支持自定义技能的 AI 编码助手 / 代理工具,在生成、审查或重构 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!("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();

// 收集 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("互斥锁中毒");
        *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+)
/// # 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
// 坏:使用 unsafe 但没有 Safety 注释
// 坏:在不相关的类型之间进行 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()传播错误,永远不要在库/生产代码中崩溃
解析,而非验证在边界处将非结构化数据转换为类型化结构体
新类型确保类型安全将基本类型包装在新类型中以防止参数交换
优先使用迭代器而非循环声明式链更清晰,通常也更快
对 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/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.

相关技能