npx skills add https://github.com/0xdarkmatter/claude-mods --skill rust-ops全面的 Rust 技能,涵盖所有权、异步、错误处理和生产生态系统。
Who owns the value?
│
├─ Need to transfer ownership
│ └─ Move: let s2 = s1; (s1 is invalid after this)
│
├─ Need to read without owning
│ └─ Shared borrow: &T (multiple allowed, no mutation)
│
├─ Need to mutate without owning
│ └─ Exclusive borrow: &mut T (only one, no other borrows)
│
├─ Need to share ownership across threads
│ └─ Arc<T> (atomic reference counting)
│ └─ Need mutation too? Arc<Mutex<T>>
│
├─ Need to share ownership single-threaded
│ └─ Rc<T> (reference counting, not Send)
│ └─ Need mutation too? Rc<RefCell<T>>
│
└─ Need to avoid cloning large data
└─ Cow<'a, T> (clone-on-write, borrows when possible)
&mut T 或者 任意数量的 &TWhat kind of error?
│
├─ Operation might not have a value (no error info needed)
│ └─ Option<T>: Some(value) or None
│
├─ Library code (callers need to match on error variants)
│ └─ thiserror: #[derive(Error)] enum with variants
│ └─ Each variant can wrap source errors with #[from]
│
├─ Application code (just need context, not matching)
│ └─ anyhow: anyhow::Result<T>, .context("msg")
│
├─ Converting between error types
│ └─ impl From<SourceError> for MyError
│ └─ Or use #[from] with thiserror
│
└─ Truly unrecoverable (violating invariants)
└─ panic!() or unwrap() - avoid in library code
Comprehensive Rust skill covering ownership, async, error handling, and the production ecosystem.
Who owns the value?
│
├─ Need to transfer ownership
│ └─ Move: let s2 = s1; (s1 is invalid after this)
│
├─ Need to read without owning
│ └─ Shared borrow: &T (multiple allowed, no mutation)
│
├─ Need to mutate without owning
│ └─ Exclusive borrow: &mut T (only one, no other borrows)
│
├─ Need to share ownership across threads
│ └─ Arc<T> (atomic reference counting)
│ └─ Need mutation too? Arc<Mutex<T>>
│
├─ Need to share ownership single-threaded
│ └─ Rc<T> (reference counting, not Send)
│ └─ Need mutation too? Rc<RefCell<T>>
│
└─ Need to avoid cloning large data
└─ Cow<'a, T> (clone-on-write, borrows when possible)
&mut T or any number of &T广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("not found: {entity} with id {id}")]
NotFound { entity: &'static str, id: i64 },
#[error("validation failed: {0}")]
Validation(String),
}
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<Config> {
let content = std::fs::read_to_string(path)
.context("failed to read config file")?;
let config: Config = toml::from_str(&content)
.context("failed to parse config")?;
Ok(config)
}
// ? on Result: returns Err early, unwraps Ok
let file = File::open(path)?;
// ? on Option: returns None early, unwraps Some
let first = items.first()?;
// Chain with map_err for context
let port: u16 = env::var("PORT")
.map_err(|_| AppError::Config("PORT not set"))?
.parse()
.map_err(|_| AppError::Config("PORT not a number"))?;
深入探讨:加载 ./references/error-handling.md 以了解 Result/Option 组合器、错误转换模式、panic/恢复。
#[derive(Debug, Clone, PartialEq, Eq, Hash)] // Value types
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] // API types
#[derive(Debug, thiserror::Error)] // Error types
| Trait 对象 (dyn Trait) | 泛型 (T: Trait)
---|---|---
分发方式 | 动态(虚表) | 静态(单态化)
二进制大小 | 较小 | 较大(每种类型副本)
性能 | 轻微开销 | 零成本
异构集合 | 支持 | 不支持
使用场景 | 运行时多态、插件系统 | 性能关键、类型已知
// Generics (preferred when types known at compile time)
fn process<T: Display>(item: T) { println!("{item}"); }
// Trait objects (when you need heterogeneous collections)
fn process_all(items: &[Box<dyn Display>]) {
for item in items { println!("{item}"); }
}
| Trait | 用途 | 自动派生? |
|---|---|---|
Debug | 调试格式化 | 是 |
Clone | 显式复制 | 是 |
Copy | 隐式复制(小、仅栈上) | 是 |
Display | 面向用户的格式化 | 否(手动实现) |
From/Into | 类型转换 | 否(实现 From,免费获得 Into) |
Send | 可在线程间安全发送 | 自动 |
Sync | 可在线程间安全共享引用 | 自动 |
Deref | 智能指针解引用 | 否 |
Iterator | 迭代协议 | 否 |
Default | 默认值 | 是 |
深入探讨:加载 ./references/traits-generics.md 以了解关联类型、超 trait、密封 trait、扩展 trait。
Do you need async?
│
├─ I/O-heavy (network, files, databases)
│ └─ Yes. Use tokio.
│
├─ CPU-heavy computation
│ └─ No. Use rayon for data parallelism.
│ └─ Or tokio::task::spawn_blocking for mixing with async
│
├─ Simple scripts or CLI tools
│ └─ Probably not. Blocking I/O is fine.
│
└─ Yes, I need async:
│
├─ Runtime: tokio (dominant), or async-std
├─ HTTP client: reqwest
├─ HTTP server: axum (tower-based) or actix-web
├─ Database: sqlx (compile-time checked)
└─ Structured logging: tracing
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Spawn concurrent tasks
let (a, b) = tokio::join!(
fetch_users(),
fetch_orders(),
);
// Select first to complete
tokio::select! {
result = long_operation() => handle(result),
_ = tokio::time::sleep(Duration::from_secs(5)) => {
eprintln!("timeout");
}
}
Ok(())
}
| 通道 | 使用场景 | 导入 |
|---|---|---|
mpsc | 多生产者,单消费者 | tokio::sync::mpsc |
oneshot | 单值,单次使用 | tokio::sync::oneshot |
broadcast | 多消费者,所有消息都接收 | tokio::sync::broadcast |
watch | 单值,仅最新值(配置重载) | tokio::sync::watch |
深入探讨:加载 ./references/async-tokio.md 以了解生成模式、优雅关闭、Mutex 选择、异步 trait、流。
# Create project
cargo new my-project # binary
cargo new my-lib --lib # library
# Build and run
cargo build # debug
cargo build --release # optimized
cargo run -- args # build + run
cargo run --example name # run example
# Test
cargo test # all tests
cargo test test_name # specific test
cargo test -- --nocapture # show println output
# Dependencies
cargo add serde --features derive # add dep
cargo add tokio -F full # shorthand
cargo update # update lock file
# Check without building
cargo check # fast type checking
cargo clippy # lints
cargo fmt # format
# Workspace
cargo test --workspace # test all crates
cargo build -p my-crate # build specific crate
[features]
default = ["json"]
json = ["dep:serde_json"]
full = ["json", "yaml", "toml"]
[dependencies]
serde_json = { version = "1", optional = true }
| 陷阱 | 原因 | 修复方法 |
|---|---|---|
String vs &str | 拥有 vs 借用,函数签名 | 参数接受 &str,返回 String |
| 借用检查器冲突 | 在修改时借用自身 | 拆分结构体、使用索引、克隆(如果成本低) |
| 生命周期省略混淆 | 函数签名中隐藏的生命周期 | 显式写出以理解,然后省略 |
返回 impl Trait | 不同分支必须返回相同类型 | 使用 Box<dyn Trait> 进行异构返回 |
tokio::Mutex vs std::Mutex | std::Mutex 不能跨 .await 持有 | 在 await 点使用 tokio::Mutex |
| 孤儿规则 | 不能为外部类型实现外部 trait | 新类型模式:struct Wrapper(ForeignType) |
Pin 混淆 | 自引用异步 future 所需 | 使用 Box::pin(),不要对抗它 |
异步上的 Send 约束 | 生成的 future 必须是 Send | 在异步中避免 Rc、RefCell;使用 Arc、Mutex |
生产环境中的 .unwrap() | 在 None/Err 时 panic | 使用 ?、.unwrap_or()、.expect("reason") |
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct User {
user_id: i64,
display_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
#[serde(default)]
is_active: bool,
#[serde(rename = "type")]
user_type: UserType,
#[serde(with = "chrono::serde::ts_seconds")]
created_at: DateTime<Utc>,
}
// Serialize
let json = serde_json::to_string(&user)?;
let yaml = serde_yaml::to_string(&user)?;
// Deserialize
let user: User = serde_json::from_str(&json)?;
深入探讨:加载 ./references/ecosystem.md 以了解 serde 高级用法、clap、reqwest、sqlx、axum、tracing、rayon。
加载这些文件以深入了解相关主题。每个文件都是独立的。
| 参考文件 | 何时加载 |
|---|---|
./references/ownership-lifetimes.md | 借用规则、生命周期注解、省略、内部可变性、常见借用检查器模式 |
./references/traits-generics.md | Trait 设计、关联类型、超 trait、泛型、约束、密封/扩展 trait |
./references/error-handling.md | Result/Option 组合器、thiserror/anyhow 深入探讨、错误转换、panic/恢复 |
./references/async-tokio.md | tokio 运行时、生成、通道、选择、流、优雅关闭、异步 trait、Mutex 选择 |
./references/ecosystem.md | serde 高级用法、clap、reqwest、sqlx、axum、tracing、rayon、itertools、Cow |
./references/testing.md | 单元/集成/文档测试、异步测试、mockall、proptest、criterion 基准测试 |
docker-ops - Rust 的多阶段构建(scratch/distroless,用于层缓存的 cargo-chef)ci-cd-ops - Rust CI 流水线、cargo 缓存、交叉编译testing-ops - 跨语言测试策略每周安装次数
1
仓库
GitHub 星标数
8
首次出现
1 天前
安全审计
安装于
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1
What kind of error?
│
├─ Operation might not have a value (no error info needed)
│ └─ Option<T>: Some(value) or None
│
├─ Library code (callers need to match on error variants)
│ └─ thiserror: #[derive(Error)] enum with variants
│ └─ Each variant can wrap source errors with #[from]
│
├─ Application code (just need context, not matching)
│ └─ anyhow: anyhow::Result<T>, .context("msg")
│
├─ Converting between error types
│ └─ impl From<SourceError> for MyError
│ └─ Or use #[from] with thiserror
│
└─ Truly unrecoverable (violating invariants)
└─ panic!() or unwrap() - avoid in library code
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("not found: {entity} with id {id}")]
NotFound { entity: &'static str, id: i64 },
#[error("validation failed: {0}")]
Validation(String),
}
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<Config> {
let content = std::fs::read_to_string(path)
.context("failed to read config file")?;
let config: Config = toml::from_str(&content)
.context("failed to parse config")?;
Ok(config)
}
// ? on Result: returns Err early, unwraps Ok
let file = File::open(path)?;
// ? on Option: returns None early, unwraps Some
let first = items.first()?;
// Chain with map_err for context
let port: u16 = env::var("PORT")
.map_err(|_| AppError::Config("PORT not set"))?
.parse()
.map_err(|_| AppError::Config("PORT not a number"))?;
Deep dive : Load ./references/error-handling.md for Result/Option combinators, error conversion patterns, panic/recover.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] // Value types
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] // API types
#[derive(Debug, thiserror::Error)] // Error types
| Trait Objects (dyn Trait) | Generics (T: Trait)
---|---|---
Dispatch | Dynamic (vtable) | Static (monomorphized)
Binary size | Smaller | Larger (per-type copies)
Performance | Slight overhead | Zero-cost
Heterogeneous collections | Yes | No
Use when | Runtime polymorphism, plugin systems | Performance-critical, known types
// Generics (preferred when types known at compile time)
fn process<T: Display>(item: T) { println!("{item}"); }
// Trait objects (when you need heterogeneous collections)
fn process_all(items: &[Box<dyn Display>]) {
for item in items { println!("{item}"); }
}
| Trait | Purpose | Auto-derive? |
|---|---|---|
Debug | Debug formatting | Yes |
Clone | Explicit copy | Yes |
Copy | Implicit copy (small, stack-only) | Yes |
Display | User-facing formatting | No (impl manually) |
From/Into | Type conversion | No (impl From, get Into free) |
Send | Safe to send between threads | Auto |
Sync | Safe to share references between threads | Auto |
Deref | Smart pointer dereference | No |
Iterator | Iteration protocol | No |
Default | Default value | Yes |
Deep dive : Load ./references/traits-generics.md for associated types, supertraits, sealed traits, extension traits.
Do you need async?
│
├─ I/O-heavy (network, files, databases)
│ └─ Yes. Use tokio.
│
├─ CPU-heavy computation
│ └─ No. Use rayon for data parallelism.
│ └─ Or tokio::task::spawn_blocking for mixing with async
│
├─ Simple scripts or CLI tools
│ └─ Probably not. Blocking I/O is fine.
│
└─ Yes, I need async:
│
├─ Runtime: tokio (dominant), or async-std
├─ HTTP client: reqwest
├─ HTTP server: axum (tower-based) or actix-web
├─ Database: sqlx (compile-time checked)
└─ Structured logging: tracing
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Spawn concurrent tasks
let (a, b) = tokio::join!(
fetch_users(),
fetch_orders(),
);
// Select first to complete
tokio::select! {
result = long_operation() => handle(result),
_ = tokio::time::sleep(Duration::from_secs(5)) => {
eprintln!("timeout");
}
}
Ok(())
}
| Channel | Use Case | Import |
|---|---|---|
mpsc | Multiple producers, single consumer | tokio::sync::mpsc |
oneshot | Single value, single use | tokio::sync::oneshot |
broadcast | Multiple consumers, all get every message | tokio::sync::broadcast |
watch | Single value, latest-only (config reload) | tokio::sync::watch |
Deep dive : Load ./references/async-tokio.md for spawn patterns, graceful shutdown, Mutex choice, async traits, streams.
# Create project
cargo new my-project # binary
cargo new my-lib --lib # library
# Build and run
cargo build # debug
cargo build --release # optimized
cargo run -- args # build + run
cargo run --example name # run example
# Test
cargo test # all tests
cargo test test_name # specific test
cargo test -- --nocapture # show println output
# Dependencies
cargo add serde --features derive # add dep
cargo add tokio -F full # shorthand
cargo update # update lock file
# Check without building
cargo check # fast type checking
cargo clippy # lints
cargo fmt # format
# Workspace
cargo test --workspace # test all crates
cargo build -p my-crate # build specific crate
[features]
default = ["json"]
json = ["dep:serde_json"]
full = ["json", "yaml", "toml"]
[dependencies]
serde_json = { version = "1", optional = true }
| Gotcha | Why | Fix |
|---|---|---|
String vs &str | Owned vs borrowed, function signatures | Accept &str in params, return String |
| Borrow checker fight | Borrowing self while mutating | Split struct, use indices, clone (if cheap) |
| Lifetime elision confusion | Hidden lifetimes in function signatures | Write them out explicitly to understand, then elide |
impl Trait in return | Different branches must return same type | Use Box<dyn Trait> for heterogeneous returns |
tokio::Mutex vs std::Mutex | std::Mutex can't be held across .await | Use tokio::Mutex across await points |
| Orphan rule | Can't impl foreign trait for foreign type | Newtype pattern: struct Wrapper(ForeignType) |
Pin confusion | Required for self-referential async futures | Use Box::pin(), don't fight it |
Send bounds on async | Spawned futures must be Send | Avoid Rc, RefCell in async; use Arc, Mutex |
.unwrap() in production | Panics on None/Err | Use ?, .unwrap_or(), .expect("reason") |
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct User {
user_id: i64,
display_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
#[serde(default)]
is_active: bool,
#[serde(rename = "type")]
user_type: UserType,
#[serde(with = "chrono::serde::ts_seconds")]
created_at: DateTime<Utc>,
}
// Serialize
let json = serde_json::to_string(&user)?;
let yaml = serde_yaml::to_string(&user)?;
// Deserialize
let user: User = serde_json::from_str(&json)?;
Deep dive : Load ./references/ecosystem.md for serde advanced usage, clap, reqwest, sqlx, axum, tracing, rayon.
Load these for deep-dive topics. Each is self-contained.
| Reference | When to Load |
|---|---|
./references/ownership-lifetimes.md | Borrowing rules, lifetime annotations, elision, interior mutability, common borrow checker patterns |
./references/traits-generics.md | Trait design, associated types, supertraits, generics, constraints, sealed/extension traits |
./references/error-handling.md | Result/Option combinators, thiserror/anyhow deep dive, error conversion, panic/recover |
./references/async-tokio.md | tokio runtime, spawn, channels, select, streams, graceful shutdown, async traits, Mutex choice |
./references/ecosystem.md | serde advanced, clap, reqwest, sqlx, axum, tracing, rayon, itertools, Cow |
./references/testing.md | Unit/integration/doc tests, async tests, mockall, proptest, criterion benchmarks |
docker-ops - Multi-stage builds for Rust (scratch/distroless, cargo-chef for layer caching)ci-cd-ops - Rust CI pipelines, cargo caching, cross-compilationtesting-ops - Cross-language testing strategiesWeekly Installs
1
Repository
GitHub Stars
8
First Seen
1 day ago
Security Audits
Installed on
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1
AI新闻播客制作技能:实时新闻转对话式播客脚本与音频生成
1,200 周安装