Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Rust coding best practices for idiomatic, efficient, and maintainable code. Use when writing Rust code, reviewing code, or learning Rust patterns.
.claude/skills/pgdogdev-rust/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 165% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 64% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 96% | 0% |
Guidelines for writing idiomatic, efficient, and maintainable Rust code.
Use thiserror
rustuse thiserror::Error; #[derive(Error, Debug)] pub enum ConfigError { #[error("Failed to read config: {0}")] Io(#[from] std::io::Error), #[error("Failed to parse config: {0}")] Parse(#[from] toml::de::Error), #[error("Invalid configuration: {message}")] Invalid { message: String }, }
.unwrap()rust// BAD let value = map.get("key").unwrap(); // GOOD let value = map.get("key").ok_or_else(|| Error::MissingKey("key"))?; // GOOD (when None is truly impossible) let value = map.get("key").expect("key always present after init");
rust// BAD - unnecessary clone fn process(data: String) { ... } process(my_string.clone()); // GOOD - borrow when possible fn process(data: &str) { ... } process(&my_string);
Cow for Flexible Ownershiprustuse std::borrow::Cow; fn process(data: Cow<'_, str>) -> Cow<'_, str> { if data.contains("bad") { Cow::Owned(data.replace("bad", "good")) } else { data // No allocation if unchanged } }
rust// GOOD - clear ownership impl User { pub fn new(name: impl Into<String>) -> Self { Self { name: name.into() } } }
rust#[derive(Default)] pub struct ServerBuilder { host: Option<String>, port: Option<u16>, timeout: Option<Duration>, } impl ServerBuilder { pub fn host(mut self, host: impl Into<String>) -> Self { self.host = Some(host.into()); self } pub fn port(mut self, port: u16) -> Self { self.port = Some(port); self } pub fn build(self) -> Result<Server, ConfigError> { Ok(Server { host: self.host.unwrap_or_else(|| "localhost".into()), port: self.port.ok_or(ConfigError::MissingPort)?, timeout: self.timeout.unwrap_or(Duration::from_secs(30)), }) } }
rust// BAD - easy to mix up fn transfer(from: i64, to: i64, amount: i64) { ... } // GOOD - compile-time safety pub struct AccountId(i64); pub struct Amount(i64); fn transfer(from: AccountId, to: AccountId, amount: Amount) { ... }
#[must_use] for Important Returnsrust#[must_use] pub fn validate(&self) -> Result<(), ValidationError> { // ... }
rust// BAD let mut results = Vec::new(); for item in items { if item.is_valid() { results.push(item.transform()); } } // GOOD let results: Vec<_> = items .into_iter() .filter(|item| item.is_valid()) .map(|item| item.transform()) .collect();
collect() Type Inferencerust// Collect into Vec let vec: Vec<_> = iter.collect(); // Collect into HashMap let map: HashMap<_, _> = iter.collect(); // Collect Results let results: Result<Vec<_>, _> = iter.collect();
tokio for Async Runtimerust#[tokio::main] async fn main() -> Result<()> { let result = fetch_data().await?; Ok(()) }
rust// BAD - blocks the runtime async fn bad() { std::thread::sleep(Duration::from_secs(1)); } // GOOD - async sleep async fn good() { tokio::time::sleep(Duration::from_secs(1)).await; } // GOOD - spawn blocking for CPU-intensive work async fn compute() -> i32 { tokio::task::spawn_blocking(|| expensive_computation()).await.unwrap() }
rust#[cfg(test)] mod tests { use super::*; #[test] fn test_basic() { assert_eq!(add(1, 2), 3); } #[test] fn test_edge_case() { assert!(validate("").is_err()); } }
tests/rust// tests/integration_test.rs use my_crate::public_api; #[test] fn test_full_workflow() { let result = public_api::process("input"); assert!(result.is_ok()); }
assert! Macros Effectivelyrustassert!(condition); assert_eq!(left, right); assert_ne!(left, right); assert!(result.is_ok()); assert!(result.is_err()); assert_matches!(value, Pattern::Variant { .. });
rust// BAD - allocates even if not needed fn maybe_string() -> String { String::from("default") } // GOOD - return static str when possible fn maybe_string() -> &'static str { "default" }
Vec::with_capacity for Known Sizesrust// BAD - multiple reallocations let mut vec = Vec::new(); for i in 0..1000 { vec.push(i); } // GOOD - single allocation let mut vec = Vec::with_capacity(1000); for i in 0..1000 { vec.push(i); }
rust/// BAD let v = tokio::net::TcpStream::connect("localhost:8080"); /// GOOD use tokio::net::TcpStream; let v = TcpStream::connect("localhost:8080");
bashcargo build --release cargo flamegraph # requires cargo-flamegraph
rust// src/lib.rs pub mod config; pub mod client; pub mod error; // Re-export public API pub use config::Config; pub use client::Client; pub use error::Error;
pub(crate) for Internal APIsrust// Public to crate, not external users pub(crate) fn internal_helper() { ... }
rust/// Creates a new client with the given configuration. /// /// # Arguments /// /// * `config` - The client configuration /// /// # Errors /// /// Returns an error if the configuration is invalid. /// /// # Examples /// /// ``` /// let client = Client::new(Config::default())?; /// ``` pub fn new(config: Config) -> Result<Self> { // ... }
| Anti-Pattern | Better Approach | |--------------|-----------------| | .unwrap() everywhere | Use ? operator | | clone() to satisfy borrow checker | Restructure ownership | | String parameters | Use &str or impl Into<String> | | Boolean parameters | Use enums | | Long function bodies | Extract to smaller functions | | Deep nesting | Use early returns | | Magic numbers | Use named constants |
bash# Quality gates cargo fmt -- --check && cargo clippy -- -D warnings && cargo test # Common cargo commands cargo check # Fast syntax/type check cargo build # Debug build cargo build --release # Release build cargo nextest run # Run tests cargo doc --open # Generate and view docs cargo clippy --fix # Auto-fix lint issues
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 14,312 | 12,785 | -11% | 1 | 1 | 0% | 2,774 | 4,791 | +73% | 0 | 0 | — |
case-02 | fail→pass | 19,041 | 45,369 | +138% | 1 | 1 | 0% | 3,860 | 5,202 | +35% | 0 | 0 | — |
case-03 | pass→pass | 17,121 | 13,109 | -23% | 1 | 1 | 0% | 2,824 | 4,642 | +64% | 0 | 0 | — |
case-04 | pass→pass | 12,290 | 9,985 | -19% | 1 | 1 | 0% | 2,077 | 4,066 | +96% | 0 | 0 | — |
case-05 | pass→pass | 13,940 | 16,672 | +20% | 1 | 1 | 0% | 2,681 | 5,212 | +94% | 0 | 0 | — |
case-06 | pass→pass | 10,188 | 5,082 | -50% | 1 | 1 | 0% | 1,785 | 3,099 | +74% | 0 | 0 | — |
case-07 | pass→pass | 3,720 | 3,902 | +5% | 1 | 1 | 0% | 608 | 2,765 | +355% | 0 | 0 | — |
case-08 | pass→pass | 6,910 | 4,463 | -35% | 1 | 1 | 0% | 1,268 | 2,908 | +129% | 0 | 0 | — |
case-09 | pass→pass | 10,651 | 5,927 | -44% | 1 | 1 | 0% | 1,991 | 3,210 | +61% | 0 | 0 | — |
case-10 | pass→pass | 9,272 | 5,427 | -41% | 1 | 1 | 0% | 1,805 | 3,095 | +71% | 0 | 0 | — |
case-11 | pass→pass | 13,631 | 11,322 | -17% | 1 | 1 | 0% | 2,470 | 4,598 | +86% | 0 | 0 | — |
case-12 | pass→pass | 4,103 | 2,844 | -31% | 1 | 1 | 0% | 729 | 2,588 | +255% | 0 | 0 | — |
case-13 | pass→pass | 6,527 | 3,683 | -44% | 1 | 1 | 0% | 1,258 | 2,841 | +126% | 0 | 0 | — |
case-14 | fail→pass | 5,468 | 2,876 | -47% | 1 | 1 | 0% | 1,039 | 2,750 | +165% | 0 | 0 | — |
case-15 | pass→pass | 8,588 | 47,886 | +458% | 1 | 1 | 0% | 1,894 | 2,716 | +43% | 0 | 0 | — |
case-16 | pass→pass | 8,640 | 50,157 | +481% | 1 | 1 | 0% | 1,766 | 3,139 | +78% | 0 | 0 | — |
case-17 | pass→pass | 12,028 | 7,594 | -37% | 1 | 1 | 0% | 2,346 | 3,478 | +48% | 0 | 0 | — |
case-18 | pass→pass | 5,316 | 2,385 | -55% | 1 | 1 | 0% | 974 | 2,562 | +163% | 0 | 0 | — |
case-19 | pass→pass | 3,601 | 3,225 | -10% | 1 | 1 | 0% | 635 | 2,598 | +309% | 0 | 0 | — |
case-20 | pass→pass | 8,195 | 4,691 | -43% | 1 | 1 | 0% | 1,700 | 3,055 | +80% | 0 | 0 | — |
case-21 | pass→pass | 4,428 | 2,937 | -34% | 1 | 1 | 0% | 628 | 2,640 | +320% | 0 | 0 | — |
case-22 | pass→pass | 4,687 | 2,622 | -44% | 1 | 1 | 0% | 637 | 2,638 | +314% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.