Rust Patterns

affaan-m/ECC/docs/zh-CN/skills/rust-patterns

作者 affaan-mef648e01899ba3e8dc6371642deaaf64b4477775無授權條款275K 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫4 天前更新

地道的Rust模式、所有权、错误处理、特质、并发,以及构建安全、高性能应用程序的最佳实践。

AI 產生的概覽

提供道地 Rust 開發指引,涵蓋所有權、錯誤處理、特質、並行與模組結構。

功能
此技能提供撰寫道地 Rust 的參考指引與程式碼範例,圍繞所有權與借用、以 Result 為基礎的錯誤處理、列舉與窮盡比對、特質與泛型、資料建模、迭代器、並行、unsafe 程式碼以及 crate 結構。它也列出 cargo 工具指令、慣用法速查表以及應避免的反模式。它產出的是建議與範例片段,而非產生的檔案或可執行成品。
適用情境
適用於撰寫新的 Rust 程式碼、審查或重構現有 Rust 程式碼,以及設計 crate 與模組配置時。也適合處理 Rust 中所有權、錯誤傳播、特質設計或安全並行相關問題。
執行需求
不隨附任何指令碼或工具,僅為說明性內容。範例涉及 thiserror、anyhow、tokio、reqwest 等 Rust crate 以及 cargo 指令,但技能本身不執行任何內容。

Rust 开发模式

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

何时使用

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

工作原理

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

Trait 和泛型

接受泛型,返回具体类型

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

使用 Trait 对象进行动态分发

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

模块系统和 Crate 结构

按领域组织,而非按类型

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,并让类型系统为你工作。

來源與署名

來源:affaan-m/ECC位於docs/zh-CN/skills/rust-patterns提交ef648e0

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架