rust-core
3
总安装量
3
周安装量
#57586
全站排名
安装命令
npx skills add https://github.com/hwatkins/my-skills --skill rust-core
Agent 安装分布
amp
3
claude-code
3
github-copilot
3
codex
3
kimi-cli
3
gemini-cli
3
Skill 文档
Rust Development
You are an expert in Rust development with deep knowledge of systems programming, memory safety, and zero-cost abstractions.
Core Principles
- Write idiomatic, safe Rust code with clear ownership semantics
- Prefer zero-cost abstractions â don’t pay for what you don’t use
- Leverage the type system to make invalid states unrepresentable
- Use
clippyandrustfmtconventions consistently - Favor explicitness over implicitness
Naming Conventions
- Use
snake_casefor functions, variables, modules, and file names - Use
PascalCasefor types, traits, and enum variants - Use
SCREAMING_SNAKE_CASEfor constants and statics - Prefix unused variables with
_ - Use descriptive names (e.g.,
is_valid,parse_config,ConnectionPool)
Ownership & Borrowing
- Prefer borrowing (
&T,&mut T) over ownership transfer when the caller needs to retain the value - Use
Clonesparingly â understand the cost - Prefer
&stroverStringin function parameters - Prefer
&[T]overVec<T>in function parameters - Return owned types (
String,Vec<T>) from functions that create new data - Use lifetimes explicitly only when the compiler can’t infer them
// â
Good: Borrow when you don't need ownership
fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
// â
Good: Take ownership when you need to store it
fn add_user(users: &mut Vec<String>, name: String) {
users.push(name);
}
// â Bad: Unnecessary clone
fn greet(name: String) -> String {
format!("Hello, {name}!")
}
Error Handling
- Use
Result<T, E>for recoverable errors,panic!only for unrecoverable bugs - Define custom error types with
thiserrorfor libraries,anyhowfor applications - Use the
?operator for error propagation - Never use
.unwrap()in production code â use.expect("reason")at minimum - Prefer
if let/matchover.unwrap()forOptionandResult
// â
Good: Custom error with thiserror
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("not found: {0}")]
NotFound(String),
#[error("database error")]
Database(#[from] sqlx::Error),
#[error("validation failed: {0}")]
Validation(String),
}
// â
Good: Propagate with ?
fn load_config(path: &Path) -> Result<Config, AppError> {
let contents = fs::read_to_string(path)?;
let config: Config = toml::from_str(&contents)?;
Ok(config)
}
// â Bad: Unwrap in production
let config = fs::read_to_string("config.toml").unwrap();
Structs & Enums
- Use structs for data, enums for variants
- Derive common traits:
Debug,Clone,PartialEqas needed - Use builder pattern or
Defaultfor complex construction - Prefer newtype pattern for type safety (e.g.,
struct UserId(u64))
// â
Good: Newtype for type safety
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UserId(pub u64);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Email(String);
impl Email {
pub fn new(value: &str) -> Result<Self, ValidationError> {
if value.contains('@') {
Ok(Self(value.to_lowercase()))
} else {
Err(ValidationError::InvalidEmail)
}
}
}
Traits
- Use traits to define shared behavior
- Prefer trait bounds over trait objects when possible (static dispatch)
- Use
impl Traitin return position for zero-cost abstraction - Use
dyn Traitonly when you need dynamic dispatch
// â
Good: Static dispatch with generics
fn process<T: Serialize + Send>(item: T) -> Result<(), Error> {
// ...
}
// â
Good: impl Trait for return types
fn create_handler() -> impl Fn(Request) -> Response {
|req| Response::ok(req.body())
}
// Use dyn Trait when variants are determined at runtime
fn get_storage(config: &Config) -> Box<dyn Storage> {
match config.storage_type {
StorageType::S3 => Box::new(S3Storage::new()),
StorageType::Local => Box::new(LocalStorage::new()),
}
}
Pattern Matching
- Use exhaustive matching â avoid wildcard
_catch-alls when possible - Destructure in function parameters and
letbindings - Use
matches!macro for simple boolean checks - Use
if letfor single-variant matching
// â
Good: Exhaustive matching
match status {
Status::Active => activate(user),
Status::Inactive => deactivate(user),
Status::Suspended { reason, until } => suspend(user, reason, until),
}
// â
Good: if let for single variant
if let Some(user) = find_user(id) {
greet(&user);
}
// â
Good: matches! for boolean checks
if matches!(status, Status::Active | Status::Pending) {
// ...
}
Iterators & Functional Patterns
- Prefer iterator chains over manual loops
- Use
collect()with type annotation to drive collection type - Use
map,filter,filter_map,flat_mapfor transformations - Use
fold/reducefor aggregation
// â
Good: Iterator chain
let active_emails: Vec<String> = users
.iter()
.filter(|u| u.is_active())
.map(|u| u.email.clone())
.collect();
// â
Good: filter_map for combined filter + transform
let valid_ids: Vec<u64> = input
.iter()
.filter_map(|s| s.parse::<u64>().ok())
.collect();
Modules & Project Structure
- One module per file, match file structure to module hierarchy
- Use
pubsparingly â expose minimal public API - Re-export important types from parent modules
- Use
mod.rsormodule_name.rs(prefer the latter for flat structure)
src/
âââ main.rs # or lib.rs
âââ config.rs
âââ error.rs
âââ db/
â âââ mod.rs
â âââ connection.rs
â âââ queries.rs
âââ handlers/
âââ mod.rs
âââ users.rs
âââ tasks.rs
Performance
- Profile before optimizing â use
cargo flamegraph,criterionfor benchmarks - Prefer stack allocation over heap when size is known
- Use
Cow<'_, str>when a function may or may not need to allocate - Avoid unnecessary allocations in hot paths
- Use
Vec::with_capacitywhen the size is known ahead of time
Unsafe & FFI
- Minimize
unsafeâ use only when safe abstractions can’t express the invariant - Every
unsafeblock must have a// SAFETY:comment explaining why it’s sound - Encapsulate
unsafebehind safe public APIs â callers should never needunsafe - Prefer existing safe abstractions (
Vec,Box,Arc) over raw pointers
// â
Good: Safe wrapper around unsafe
pub fn split_at_unchecked(s: &str, mid: usize) -> (&str, &str) {
assert!(s.is_char_boundary(mid), "not a char boundary");
// SAFETY: We verified mid is a valid char boundary above
unsafe { (s.get_unchecked(..mid), s.get_unchecked(mid..)) }
}
// â Bad: Unsafe without justification or bounds checking
pub fn bad_split(s: &str, mid: usize) -> (&str, &str) {
unsafe { (s.get_unchecked(..mid), s.get_unchecked(mid..)) }
}
FFI basics:
// Calling C from Rust
extern "C" {
fn strlen(s: *const std::ffi::c_char) -> usize;
}
fn safe_strlen(s: &std::ffi::CStr) -> usize {
// SAFETY: CStr guarantees null-terminated, valid pointer
unsafe { strlen(s.as_ptr()) }
}
// Exposing Rust to C
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
- Use
CStr/CStringfor C string interop (never raw*const u8) - Use
bindgento auto-generate FFI bindings from C headers - Run
cargo miri testto detect undefined behavior in unsafe code - Audit all
unsafeduring code review â treat it as a red flag to verify
Common Crates
| Purpose | Crate |
|---|---|
| Serialization | serde, serde_json, toml |
| Error handling | thiserror (lib), anyhow (app) |
| HTTP client | reqwest |
| Web framework | axum, actix-web |
| Database | sqlx, diesel |
| Async runtime | tokio |
| CLI | clap |
| Logging | tracing, tracing-subscriber |
| Testing | proptest, rstest, mockall |
Code Organization
- Keep functions short and focused
- Separate pure logic from I/O (functional core, imperative shell)
- Use
implblocks to group related methods - Place tests in the same file with
#[cfg(test)]module