Anti-Patterns
Layer 2: Design Choices
Core Question
Is this pattern hiding a design problem?
When reviewing code:
- Is this solving the symptom or the cause?
- Is there a more idiomatic approach?
- Does this fight or flow with Rust?
Anti-Pattern → Better Pattern
| Anti-Pattern | Why Bad | Better |
|---|---|---|
.clone() everywhere |
Hides ownership issues | Proper references or ownership |
.unwrap() in production |
Runtime panics | ?, expect, or handling |
Rc when single owner |
Unnecessary overhead | Simple ownership |
unsafe for convenience |
UB risk | Find safe pattern |
OOP via Deref |
Misleading API | Composition, traits |
| Giant match arms | Unmaintainable | Extract to methods |
String everywhere |
Allocation waste | &str, Cow<str> |
Ignoring #[must_use] |
Lost errors | Handle or let _ = |
Thinking Prompt
When seeing suspicious code:
Is this symptom or cause?
- Clone to avoid borrow? → Ownership design issue
- Unwrap "because it won't fail"? → Unhandled case
What would idiomatic code look like?
- References instead of clones
- Iterators instead of index loops
- Pattern matching instead of flags
Does this fight Rust?
- Fighting borrow checker → restructure
- Excessive unsafe → find safe pattern
Top 5 Beginner Mistakes
| Rank | Mistake | Fix |
|---|---|---|
| 1 | Clone to escape borrow checker | Use references |
| 2 | Unwrap in production | Propagate with ? |
| 3 | String for everything | Use &str |
| 4 | Index loops | Use iterators |
| 5 | Fighting lifetimes | Restructure to own data |
Code Smell → Refactoring
| Smell | Indicates | Refactoring |
|---|---|---|
Many .clone() |
Ownership unclear | Clarify data flow |
Many .unwrap() |
Error handling missing | Add proper handling |
Many pub fields |
Encapsulation broken | Private + accessors |
| Deep nesting | Complex logic | Extract methods |
| Long functions | Multiple responsibilities | Split |
| Giant enums | Missing abstraction | Trait + types |
Common Error Patterns
| Error | Anti-Pattern Cause | Fix |
|---|---|---|
| E0382 use after move | Cloning vs ownership | Proper references |
| Panic in production | Unwrap everywhere | ?, matching |
| Slow performance | String for all text | &str, Cow |
| Borrow checker fights | Wrong structure | Restructure |
| Memory bloat | Rc/Arc everywhere | Simple ownership |
Deprecated → Better
| Deprecated | Better |
|---|---|
| Index-based loops | .iter(), .enumerate() |
collect::<Vec<_>>() then iterate |
Chain iterators |
| Manual unsafe cell | Cell, RefCell |
mem::transmute for casts |
as or TryFrom |
| Custom linked list | Vec, VecDeque |
lazy_static! |
std::sync::OnceLock |
Quick Review Checklist
- No
.clone()without justification - No
.unwrap()in library code - No
pubfields with invariants - No index loops when iterator works
- No
Stringwhere&strsuffices - No ignored
#[must_use]warnings - No
unsafewithout SAFETY comment - No giant functions (>50 lines)
Source: christestet/owui-lint — distributed by TomeVault.