All skills
zhanghandong avatar

/m15-anti-pattern

@f464484
by Alexzhanghandong/rust-skills1.5k stars
115

Use when reviewing code for anti-patterns. Keywords: anti-pattern, common mistake, pitfall, code smell, bad practice, code review, is this an anti-pattern, better way to do this, common mistake to avoid, why is this bad, idiomatic way, beginner mistake, fighting borrow checker, clone everywhere, unwrap in production, should I refactor, 反模式, 常见错误, 代码异味, 最佳实践, 地道写法

Use this Skill: https://skilld.dev/gh/zhanghandong/rust-skills/m15-anti-pattern

This session only. Nothing lands on disk.

patternscommon-mistakes.md

≈2.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Common Rust Anti-Patterns & Mistakes

Ownership Anti-Patterns

1. Clone Everything

// ANTI-PATTERN: clone to avoid borrow checker
fn process(data: Vec<String>) {
    for item in data.clone() {  // unnecessary clone
        println!("{}", item);
    }
    use_data(data);
}

// BETTER: borrow when you don't need ownership
fn process(data: Vec<String>) {
    for item in &data {  // borrow instead
        println!("{}", item);
    }
    use_data(data);
}

2. Unnecessary Box

// ANTI-PATTERN: boxing everything
fn get_value() -> Box<String> {
    Box::new(String::from("hello"))
}

// BETTER: return value directly
fn get_value() -> String {
    String::from("hello")
}

3. Holding References Too Long

// ANTI-PATTERN: borrow prevents mutation
let mut data = vec![1, 2, 3];
let first = &data[0];
data.push(4);  // ERROR: data is borrowed
println!("{}", first);

// BETTER: scope the borrow
let mut data = vec![1, 2, 3];
let first = data[0];  // copy the value
data.push(4);  // OK
println!("{}", first);

Error Handling Anti-Patterns

4. Unwrap Everywhere

// ANTI-PATTERN: crashes on error
fn process_file(path: &str) {
    let content = std::fs::read_to_string(path).unwrap();
    let config: Config = toml::from_str(&content).unwrap();
}

// BETTER: propagate errors
fn process_file(path: &str) -> Result<Config, Error> {
    let content = std::fs::read_to_string(path)?;
    let config: Config = toml::from_str(&content)?;
    Ok(config)
}

5. Ignoring Errors

// ANTI-PATTERN: silent failure
let _ = file.write_all(data);

// BETTER: handle or propagate
file.write_all(data)?;
// or at minimum, log the error
if let Err(e) = file.write_all(data) {
    eprintln!("Warning: failed to write: {}", e);
}

6. Panic in Library Code

// ANTI-PATTERN: library panics
pub fn parse(input: &str) -> Data {
    if input.is_empty() {
        panic!("input cannot be empty");
    }
    // ...
}

// BETTER: return Result
pub fn parse(input: &str) -> Result<Data, ParseError> {
    if input.is_empty() {
        return Err(ParseError::EmptyInput);
    }
    // ...
}

String Anti-Patterns

7. String Instead of &str

// ANTI-PATTERN: forces allocation
fn greet(name: String) {
    println!("Hello, {}", name);
}

greet("world".to_string());  // allocation

// BETTER: accept &str
fn greet(name: &str) {
    println!("Hello, {}", name);
}

greet("world");  // no allocation

8. Format for Simple Concatenation

// ANTI-PATTERN: format overhead
let greeting = format!("{}{}", "Hello, ", name);

// BETTER for simple cases: push_str
let mut greeting = String::from("Hello, ");
greeting.push_str(name);

// Or use + for String + &str
let greeting = String::from("Hello, ") + name;

9. Repeated String Operations

// ANTI-PATTERN: O(n²) allocations
let mut result = String::new();
for word in words {
    result = result + word + " ";
}

// BETTER: join
let result = words.join(" ");

// Or with_capacity + push_str
let mut result = String::with_capacity(total_len);
for word in words {
    result.push_str(word);
    result.push(' ');
}

Collection Anti-Patterns

10. Index Instead of Iterator

// ANTI-PATTERN: bounds checking overhead
for i in 0..vec.len() {
    process(vec[i]);
}

// BETTER: iterator
for item in &vec {
    process(item);
}

11. Collect Then Iterate

// ANTI-PATTERN: unnecessary allocation
let filtered: Vec<_> = items.iter().filter(|x| x.valid).collect();
for item in filtered {
    process(item);
}

// BETTER: chain iterators
for item in items.iter().filter(|x| x.valid) {
    process(item);
}

12. Wrong Collection Type

// ANTI-PATTERN: Vec for frequent membership checks
let allowed: Vec<&str> = vec!["a", "b", "c"];
if allowed.contains(&input) { ... }  // O(n)

// BETTER: HashSet for membership
use std::collections::HashSet;
let allowed: HashSet<&str> = ["a", "b", "c"].into();
if allowed.contains(input) { ... }  // O(1)

Concurrency Anti-Patterns

13. Mutex for Read-Heavy Data

// ANTI-PATTERN: Mutex when mostly reading
let data = Arc::new(Mutex::new(config));
// All readers block each other

// BETTER: RwLock for read-heavy workloads
let data = Arc::new(RwLock::new(config));
// Multiple readers can proceed in parallel

14. Holding Lock Across Await

// ANTI-PATTERN: lock held across await
async fn bad() {
    let guard = mutex.lock().unwrap();
    some_async_op().await;  // lock held!
    use(guard);
}

// BETTER: scope the lock
async fn good() {
    let value = {
        let guard = mutex.lock().unwrap();
        guard.clone()
    };  // lock released
    some_async_op().await;
    use(value);
}

15. Blocking in Async

// ANTI-PATTERN: blocking call in async
async fn bad() {
    std::thread::sleep(Duration::from_secs(1));  // blocks executor!
}

// BETTER: async sleep
async fn good() {
    tokio::time::sleep(Duration::from_secs(1)).await;
}

// For CPU work: spawn_blocking
async fn compute() {
    tokio::task::spawn_blocking(|| heavy_work()).await
}

Type System Anti-Patterns

16. Stringly Typed

// ANTI-PATTERN: strings for everything
fn connect(host: &str, port: &str, timeout: &str) { ... }
connect("8080", "localhost", "30");  // wrong order!

// BETTER: strong types
struct Host(String);
struct Port(u16);
struct Timeout(Duration);

fn connect(host: Host, port: Port, timeout: Timeout) { ... }

17. Boolean Parameters

// ANTI-PATTERN: what does true mean?
fn fetch(url: &str, use_cache: bool, validate_ssl: bool) { ... }
fetch("https://...", true, false);  // unclear

// BETTER: builder or named parameters
struct FetchOptions {
    use_cache: bool,
    validate_ssl: bool,
}

fn fetch(url: &str, options: FetchOptions) { ... }
fetch("https://...", FetchOptions {
    use_cache: true,
    validate_ssl: false,
});

18. Option<Option<T>>

// ANTI-PATTERN: nested Option
fn find(id: u32) -> Option<Option<User>> { ... }
// What does None vs Some(None) mean?

// BETTER: use Result or custom enum
enum FindResult {
    Found(User),
    NotFound,
    Error(String),
}

API Design Anti-Patterns

19. Taking Ownership Unnecessarily

// ANTI-PATTERN: takes ownership but doesn't need it
fn validate(config: Config) -> bool {
    config.timeout > 0 && config.retries >= 0
}

// BETTER: borrow
fn validate(config: &Config) -> bool {
    config.timeout > 0 && config.retries >= 0
}

20. Returning References to Temporaries

// ANTI-PATTERN: impossible lifetime
fn get_default() -> &str {
    let s = String::from("default");
    &s  // ERROR: s is dropped
}

// BETTER: return owned
fn get_default() -> String {
    String::from("default")
}

// Or return static
fn get_default() -> &'static str {
    "default"
}

21. Overly Generic Functions

// ANTI-PATTERN: complex generics for simple function
fn process<T, U, V>(input: T) -> V
where
    T: Into<U>,
    U: AsRef<str> + Clone,
    V: From<String>,
{ ... }

// BETTER: concrete types if generics not needed
fn process(input: &str) -> String { ... }

Macro Anti-Patterns

22. Macro When Function Works

// ANTI-PATTERN: macro for simple operation
macro_rules! add {
    ($a:expr, $b:expr) => { $a + $b };
}

// BETTER: just use a function
fn add(a: i32, b: i32) -> i32 { a + b }

23. Complex Macro Without Tests

// ANTI-PATTERN: complex macro with no tests
macro_rules! define_api {
    // ... 100 lines of macro code ...
}

// BETTER: test macro outputs
#[test]
fn test_macro_expansion() {
    // Use cargo-expand or trybuild
}

Quick Reference

Anti-Pattern Better Alternative
Clone everywhere Borrow when possible
Unwrap everywhere Propagate with ?
String parameters &str parameters
Index loops Iterator loops
Collect then process Chain iterators
Mutex for reads RwLock for read-heavy
Lock across await Scope the lock
Blocking in async spawn_blocking
Stringly typed Strong types
Boolean params Builders or enums

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The analyzed skill contains only documentation, checklists, and code examples intended to help users identify and fix common Rust anti-patterns. It does not execute any code, invoke tools, or perform network/file system operations.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    2 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at f464484. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 8 months ago
user-invocable
false
  • Rust
  • Refactoring
  • anti-pattern
  • code-review
  • code-smell
  • ownership
  • error-handling
  • best-practices
  • idiomatic

README badge

README badge for zhanghandong/rust-skills

Identifies and refactors common Rust anti-patterns like excessive `.clone()`, `.unwrap()` in production code, and fighting the borrow checker. Helps code reviewers spot design issues masked by symptom-level fixes and suggest idiomatic alternatives.

Generated from the current SKILL.md.

Does this skill help me identify anti-patterns while writing code?
No. This skill is user-invocable: false, meaning it's designed for AI agents to consult during code review, not for interactive use while coding. It's best used when reviewing existing code for design problems.
What Rust anti-patterns does this skill cover?
It covers common patterns like excessive .clone() calls, .unwrap() in production code, unnecessary Rc usage, String overuse instead of &str, fighting the borrow checker, and large unmaintainable code structures. It traces problems back to underlying design issues and forward to specific fixes.
Does this skill point to other resources for fixing anti-patterns?
Yes. The skill traces anti-patterns up to layer 3 skills like m01-ownership and m06-error-handling for root cause analysis, and down to layer 1 implementation guidance. It also cross-references m09-domain for data flow design and m10-performance for optimization.
How does this differ from a generic Rust linter or clippy?
This skill focuses on design-level anti-patterns and reasoning about *why* code is problematic, not just syntax warnings. It provides mental models for identifying whether code is solving a symptom or the underlying cause, and guides refactoring strategy.

Generated from the current SKILL.md. These answers refresh after source changes.