majiayu000/spellbook-rust-best-practices
Use when writing, modifying, or reviewing Rust code. ALWAYS invoke before Rust edits; covers Microsoft Pragmatic Rust guidance for error handling, API design, performance, and idiomatic patterns.
npx skills add https://github.com/majiayu000/spellbook --skill rust-best-practices
Based on Microsoft Pragmatic Rust Guidelines and Rust community standards.
thiserror for Librariesuse thiserror::Error;
#[derive(Error, Debug)]
pub enum MyError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error at line {line}: {message}")]
Parse { line: usize, message: String },
#[error("Not found: {0}")]
NotFound(String),
}
anyhow for Applicationsuse anyhow::{Context, Result};
fn main() -> Result<()> {
let config = load_config()
.context("Failed to load configuration")?;
run_app(config)?;
Ok(())
}
// ❌ BAD
pub fn get_item(index: usize) -> &Item {
&self.items[index] // Panics on out-of-bounds
}
// ✅ GOOD
pub fn get_item(&self, index: usize) -> Option<&Item> {
self.items.get(index)
}
// ✅ GOOD - when you need Result
pub fn get_item(&self, index: usize) -> Result<&Item, Error> {
self.items.get(index).ok_or(Error::NotFound(index))
}
pub struct Client {
url: String,
timeout: Duration,
retries: u32,
}
impl Client {
pub fn builder(url: impl Into<String>) -> ClientBuilder {
ClientBuilder {
url: url.into(),
timeout: Duration::from_secs(30),
retries: 3,
}
}
}
pub struct ClientBuilder {
url: String,
timeout: Duration,
retries: u32,
}
impl ClientBuilder {
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn retries(mut self, retries: u32) -> Self {
self.retries = retries;
self
}
pub fn build(self) -> Client {
Client {
url: self.url,
timeout: self.timeout,
retries: self.retries,
}
}
}
// ❌ BAD - primitive obsession
fn create_user(name: String, email: String, age: u32) -> User { ... }
// ✅ GOOD - newtype wrappers
pub struct Username(String);
pub struct Email(String);
pub struct Age(u32);
impl Email {
pub fn new(email: impl Into<String>) -> Result<Self, ValidationError> {
let email = email.into();
if email.contains('@') {
Ok(Self(email))
} else {
Err(ValidationError::InvalidEmail)
}
}
}
fn create_user(name: Username, email: Email, age: Age) -> User { ... }
impl Trait for Flexibility// ❌ BAD - overly specific
pub fn process(items: Vec<String>) { ... }
// ✅ GOOD - accept any iterable
pub fn process(items: impl IntoIterator<Item = impl AsRef<str>>) {
for item in items {
println!("{}", item.as_ref());
}
}
Take majiayu000/spellbook-rust-best-practices from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.