| name | code-simplifier |
| description | Use when writing, reviewing, or editing Rust code in this project - applies iterator chains, Option/Result chaining, early returns, borrow-over-clone, and if-let simplifications per project conventions |
Rust Code Simplifier
Proactive pattern guide for writing simple, idiomatic Rust. Apply these patterns whenever writing or modifying Rust code — not only when asked to simplify.
Project rules: These patterns align with .claude/rules/rust-patterns.md. When in doubt, re-read the rules file.
1. Iterator Chains Over Manual Loops
Use iterator chains for transformation logic. Reserve for loops only when the body needs early error returns with ?.
let mut names = Vec::new();
for tool in tools {
if tool.is_available() {
names.push(tool.name().to_owned());
}
}
let names: Vec<String> = tools
.iter()
.filter(|t| t.is_available())
.map(|t| t.name().to_owned())
.collect();
When to keep a loop: The body calls fallible operations with ?:
for entry in entries {
let meta = entry.metadata().context("failed to read metadata")?;
process(&meta).await?;
}
2. Option/Result Chaining
Flatten nested match/if let on Option/Result into chained combinators or ?.
match config.project.as_ref() {
Some(project) => match project.settings.as_ref() {
Some(settings) => match settings.get("key") {
Some(val) => Some(val.to_owned()),
None => None,
},
None => None,
},
None => None,
}
config
.project
.as_ref()?
.settings
.as_ref()?
.get("key")
.map(|v| v.to_owned())
Combinator quick reference:
| Pattern | Use when |
|---|
? | Propagate None/Err to caller |
.map() | Transform inner value |
.and_then() | Chain operations that return Option/Result |
.unwrap_or_default() | Fallback to Default impl |
.unwrap_or_else(|| ...) | Fallback with computation |
3. Early Returns
Guard against invalid/empty states at the top. Avoid deep nesting for the main logic.
fn process(entries: &[Entry], prefix: &str) -> Vec<String> {
let mut results = Vec::new();
if !entries.is_empty() {
if !prefix.is_empty() {
for entry in entries {
if entry.name.starts_with(prefix) {
results.push(entry.name.strip_prefix(prefix).unwrap().to_owned());
}
}
}
}
results
}
fn process(entries: &[Entry], prefix: &str) -> Vec<String> {
if entries.is_empty() || prefix.is_empty() {
return Vec::new();
}
entries
.iter()
.filter(|e| e.name.starts_with(prefix))
.filter_map(|e| e.name.strip_prefix(prefix))
.map(|s| s.to_owned())
.collect()
}
Signals to look for: is_empty(), is_none(), is_err(), boolean flags checked before the real work begins.
4. Avoid Redundant Clones
Borrow instead of cloning. When ownership is needed, use .to_owned() (not .clone()). Use String::from("...") for string literals (not "...".to_string()).
fn format_tool(tool: &Tool) -> String {
let data = tool.data.clone();
let name = tool.name.clone();
format!("{}: {}", name, data)
}
fn format_tool(tool: &Tool) -> String {
format!("{}: {}", tool.name, tool.data)
}
let label = "default".to_string();
let copy = config.clone();
let label = String::from("default");
let copy = config.to_owned();
5. if let for Single-Variant Match
Use match only when both arms carry logic. Use if let when only one variant matters.
match result {
Some(val) => process(val),
None => (),
}
if let Some(val) = result {
process(val);
}
match result {
Ok(ctx) => tool.format(ctx).await,
Err(e) => log_and_report(e),
}
Common Mistakes
| Mistake | Fix |
|---|
.clone() to satisfy borrow checker | Restructure to borrow, or use .to_owned() if ownership needed |
"literal".to_string() | String::from("literal") |
Nested if/match guarding empty/none | Early return guard clause at top |
for loop that only filters + maps | Iterator chain with .filter().map().collect() |
match x { Some(v) => f(v), None => () } | if let Some(v) = x { f(v); } |
Nested match on Option chains | ? operator or .and_then() |