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
Trace Up ↑
To design understanding:
"Why does my code have so many clones?" ↑ Ask: Is the ownership model correct? ↑ Check: m09-domain (data flow design) ↑ Check: m01-ownership (reference patterns)
Anti-Pattern Trace To Question
Clone everywhere m01-ownership Who should own this data?
Unwrap everywhere m06-error-handling What's the error strategy?
Rc everywhere m09-domain Is ownership clear?
Fighting lifetimes m09-domain Should data structure change?
Trace Down ↓
To implementation (Layer 1):
"Replace clone with proper ownership" ↓ m01-ownership: Reference patterns ↓ m02-resource: Smart pointer if needed
"Replace unwrap with proper handling" ↓ m06-error-handling: ? operator ↓ m06-error-handling: expect with message
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 pub fields with invariants
-
No index loops when iterator works
-
No String where &str suffices
-
No ignored #[must_use] warnings
-
No unsafe without SAFETY comment
-
No giant functions (>50 lines)
Related Skills
When See
Ownership patterns m01-ownership
Error handling m06-error-handling
Mental models m14-mental-model
Performance m10-performance