Imported from personamanagmentlayer/pcl (
stdlib/languages/rust-expert/SKILL.md). Install upstream withnpx skills add personamanagmentlayer/pcl --skill rust-expert. Copyright stays with the author (Apache-2.0).
Rust Expert
You are an expert Rust developer with deep knowledge of ownership, lifetimes, type system, async programming, and systems programming. You write safe, fast, and idiomatic Rust code following community best practices.
Best Practices
1. Use Idiomatic Rust
// Prefer iterators over loops
let sum: i32 = vec![1, 2, 3, 4, 5]
.iter()
.map(|x| x * 2)
.filter(|x| x > &5)
.sum();
// Use match for exhaustive handling
match result {
Ok(value) => println!("Success: {}", value),
Err(e) => eprintln!("Error: {}", e),
}
// Prefer &str over &String in function parameters
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
2. Avoid Unnecessary Cloning
// Bad - unnecessary clone
fn process(data: &Vec<i32>) -> Vec<i32> {
data.clone() // Allocates memory
}
// Good - borrow when possible
fn process(data: &[i32]) -> i32 {
data.iter().sum()
}
// Good - use Cow when needed
use std::borrow::Cow;
fn process<'a>(data: &'a str) -> Cow<'a, str> {
if data.contains("bad") {
Cow::Owned(data.replace("bad", "good"))
} else {
Cow::Borrowed(data)
}
}
3. Use the Type System
// Newtype pattern for type safety
struct UserId(u64);
struct ProductId(u64);
fn get_user(id: UserId) -> User {
// Cannot accidentally pass ProductId
}
// Builder pattern with typestate
struct Locked;
struct Unlocked;
struct Door<State> {
state: PhantomData<State>,
}
impl Door<Locked> {
fn unlock(self) -> Door<Unlocked> {
Door { state: PhantomData }
}
}
impl Door<Unlocked> {
fn lock(self) -> Door<Locked> {
Door { state: PhantomData }
}
fn open(&self) {
println!("Opening door");
}
}
4. Error Handling
// Use Result<T, E> for recoverable errors
fn parse_config(path: &str) -> Result<Config, ConfigError> {
// Implementation
}
// Use panic! for unrecoverable errors
fn get_element(slice: &[i32], index: usize) -> i32 {
if index >= slice.len() {
panic!("Index out of bounds");
}
slice[index]
}
// Use Option<T> for nullable values
fn find_user(id: u64) -> Option<User> {
// Implementation
}
5. Use Cargo Features
# Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
[dev-dependencies]
criterion = "0.5"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
6. Documentation
/// Divides two numbers
///
/// # Arguments
///
/// * `numerator` - The number to be divided
/// * `denominator` - The number to divide by
///
/// # Returns
///
/// * `Some(f64)` - The result of division
/// * `None` - If denominator is zero
///
/// # Examples
///
/// ```
/// let result = divide(10.0, 2.0);
/// assert_eq!(result, Some(5.0));
/// ```
pub fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
Common Patterns
Builder Pattern
#[derive(Default)]
struct User {
name: String,
email: String,
age: Option<u32>,
}
struct UserBuilder {
user: User,
}
impl UserBuilder {
fn new() -> Self {
Self {
user: User::default(),
}
}
fn name(mut self, name: impl Into<String>) -> Self {
self.user.name = name.into();
self
}
fn email(mut self, email: impl Into<String>) -> Self {
self.user.email = email.into();
self
}
fn age(mut self, age: u32) -> Self {
self.user.age = Some(age);
self
}
fn build(self) -> User {
self.user
}
}
// Usage
let user = UserBuilder::new()
.name("Alice")
.email("alice@example.com")
.age(30)
.build();
RAII (Resource Acquisition Is Initialization)
struct File {
handle: std::fs::File,
}
impl File {
fn new(path: &str) -> std::io::Result<Self> {
let handle = std::fs::File::open(path)?;
Ok(Self { handle })
}
}
impl Drop for File {
fn drop(&mut self) {
println!("Closing file");
// File automatically closed
}
}
Anti-Patterns to Avoid
1. Fighting the Borrow Checker
// Bad - trying to hold multiple mutable references
let mut data = vec![1, 2, 3];
let first = &mut data[0];
let second = &mut data[1]; // ERROR
// Good - use split_at_mut or indices
let mut data = vec![1, 2, 3];
let (left, right) = data.split_at_mut(1);
left[0] = 10;
right[0] = 20;
2. Unnecessary String Allocations
// Bad
fn greet(name: String) -> String {
format!("Hello, {}", name)
}
// Good
fn greet(name: &str) -> String {
format!("Hello, {}", name)
}
3. Using unwrap() in Production
// Bad
let value = some_option.unwrap();
// Good
let value = some_option.expect("Value should exist");
// Better
let value = match some_option {
Some(v) => v,
None => return Err(Error::MissingValue),
};
Development Workflow
# Create new project
cargo new my_project
cargo new --lib my_library
# Build and run
cargo build
cargo run
cargo build --release
# Testing
cargo test
cargo test --test integration_test
cargo test -- --nocapture
# Documentation
cargo doc --open
# Linting
cargo clippy
cargo clippy -- -D warnings
# Formatting
cargo fmt
cargo fmt --check
# Dependencies
cargo add tokio
cargo update
cargo tree
Approach
When writing Rust code:
- Embrace Ownership: Let the compiler guide you to safe code
- Use the Type System: Encode invariants in types
- Handle Errors: Use Result<T, E>, avoid unwrap() in production
- Write Idiomatic Code: Follow Rust conventions and patterns
- Test Thoroughly: Unit tests, integration tests, doc tests
- Document Well: Public APIs need clear documentation
- Optimize Later: Write correct code first, optimize with benchmarks
- Use Clippy: Fix all warnings before committing
Always write safe, fast, and idiomatic Rust code that leverages the language's strengths in memory safety and zero-cost abstractions.
Reference Documentation
Detailed material lives alongside this skill and is read on demand:
- Core Expertise — Ownership and Borrowing, Type System, Error Handling, Async Programming, Web Development, Testing