🚀 快速安装

复制以下命令并运行,立即安装此 Skill:

npx skills add https://skills.sh/affaan-m/everything-claude-code/rust-patterns

💡 提示:需要 Node.js 和 NPM

Rust 开发模式

构建安全、高性能、可维护应用程序的惯用 Rust 模式和最佳实践。

使用时机

  • 编写新的 Rust 代码
  • 审查 Rust 代码
  • 重构现有的 Rust 代码
  • 设计 crate 结构和模块布局

工作原理

此技能在六个关键领域强制执行惯用的 Rust 约定:所有权和借用,在编译时防止数据竞争;使用 Result/? 进行错误传播,库使用 thiserror,应用程序使用 anyhow;枚举和穷尽模式匹配,使非法状态不可表示;trait 和泛型,实现零成本抽象;通过 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)
}

// 不良:错误时 panic
fn load_config_bad(path: &str) -> Config {
    let content = std::fs::read_to_string(path).unwrap(); // Panic!
    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!("worker 数量必须大于 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 和未来的变体
}

Trait 和泛型

接受泛型,返回具体类型

// 良好:泛型输入,具体输出
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)
}

// 良好:多约束的 trait 边界
fn process<T: Display + Send + 'static>(item: T) -> String {
    format!("已处理: {item}")
}

使用 trait 对象实现动态分发

// 当你需要异构集合或插件系统时使用
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> {
    // 不会意外交换 user 和 order 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("请求超时")?
    .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!("派生任务 panic: {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: 由于循环边界,索引始终小于长度
unsafe { slice.get_unchecked(index) }

何时不应该使用 Unsafe

// 不良:使用 unsafe 绕过借用检查器
// 不良:为方便而使用 unsafe
// 不良:没有 Safety 注释就使用 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() 传播错误,库/生产代码中永远不要 panic
解析,而非验证 在边界处将非结构化数据转换为类型化结构体
使用 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 fn bad_async() {
    std::thread::sleep(Duration::from_secs(1)); // 阻塞执行器!
    // 应使用:tokio::time::sleep(Duration::from_secs(1)).await;
}

记住:如果代码能编译,它很可能是正确的 —— 但前提是你要避免 unwrap(),最小化 unsafe,并让类型系统为你工作。

📄 原始文档

完整文档(英文):

https://skills.sh/affaan-m/everything-claude-code/rust-patterns

💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。