rust-ownership
Solution Patterns
Pattern 1: Value Moved After Use
let s1 = String::from("hello");
let s2 = s1;
// println!("{}", s1); // Compile error!
Root Cause: Ownership transferred from s1 to s2, s1 is no longer valid.
Solutions:
- Need two copies → use
clone() - Only need to read → pass by reference
&s1 s2is temporary → consider redesign
Pattern 2: Borrow Conflict
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // Conflict!
// println!("{}", r1);
Root Cause: Immutable and mutable borrows coexist.
Solutions:
- Ensure mutable borrow completes before creating new borrows
- Restructure code organization
Pattern 3: Lifetime Mismatch
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() { s1 } else { s2 }
}
Root Cause: Return value lifetime must be tied to one of the inputs.
Key Solutions:
- Clarify each reference's lifetime
- Use named lifetimes to express relationships
- Prefer returning owned types
Workflow
Step 1: Who Owns the Data?
| Situation | Owner |
|---|---|
| Function parameter | Caller owns |
| Function-local variable | Function owns (destroyed on return) |
| Struct field | Struct instance owns |
Arc<T> |
Multiple shared owners |
Step 2: Is Borrowing Appropriate?
| Operation | Borrow Type | Notes |
|---|---|---|
| Read-only | &T |
Multiple can coexist |
| Needs mutation | &mut T |
Only one at a time |
| Will original be modified during borrow? | If yes, that's the issue |
Step 3: Can Lifetimes Be Avoided?
Return String instead of &str
↓
Use owned collections instead of slices
↓
Use Arc/Rc for shared ownership
↓
Lifetimes aren't always necessary
Smart Pointer Selection
| Scenario | Choice | Reason |
|---|---|---|
| Heap-allocate single value | Box<T> |
Simple and direct |
| Single-threaded shared reference counting | Rc<T> |
Lightweight |
| Multi-threaded shared reference counting | Arc<T> |
Atomic operations |
| Need runtime borrow checking | RefCell<T> |
Single-threaded interior mutability |
| Multi-threaded interior mutability | Mutex<T> or RwLock<T> |
Thread-safe |
Common Pitfalls
| Anti-Pattern | Problem | Correct Approach |
|---|---|---|
.clone() everywhere |
Hides ownership issues | Think about actual ownership needs |
'static for everything |
Too loose and imprecise | Use actual required lifetimes |
Box::leak() memory leaks |
Memory waste | Use proper lifetime management |
| Fighting the borrow checker | Digging your own hole | Understand and work with compiler design |
Practical Guidance
Common Beginner Questions
1. "When should I use references vs ownership?"
- Function parameters: use references (unless consuming)
- Function returns: use references (if caller doesn't need ownership)
- Storage: consider lifetime complexity
2. "How do I add lifetime annotations?"
- Most cases: compiler can infer
- Need explicit: structs, trait impls, methods returning references
- Use meaningful names:
'connection,'file
3. "Why doesn't this borrow work?"
- Mutable borrows make original inaccessible
- Check borrow scope ranges
- Consider code reorganization
Error Code Quick Reference
| Code | Meaning | Don't Say | Ask Instead |
|---|---|---|---|
| E0382 | Use of moved value | "clone it" | Who should own this data? |
| E0597 | Lifetime too short | "extend lifetime" | Are scope boundaries correct? |
| E0506 | Borrow not ended before mutation | "end borrow first" | Where should mutation occur? |
| E0507 | Move out of reference | "clone before move" | Why move from reference? |
| E0515 | Return non-owned data | "return owned" | Should caller own the data? |
| E0716 | Temporary value lifetime insufficient | "bind to variable" | Why is this temporary? |
| E0106 | Missing lifetime parameter | "add 'a" | What's the lifetime relationship? |
Thinking Process
When encountering ownership issues, follow these steps:
1. What's this data's role in the domain?
- Entity (unique identity) → owned
- Value object (interchangeable) → clone/copy acceptable
- Temporary computation result → consider refactor
2. Is ownership design intentional or accidental?
- Intentional → work within constraints
- Accidental → consider redesign
3. Fix symptom or redesign?
- If failed 3 times → escalate to design level
Trace Up (Design Analysis)
When ownership errors persist, trace to design level:
E0382 (moved value)
↑ Ask: What design choice led to this ownership pattern?
↑ Check: Is this an entity or value object?
↑ Check: Are there other constraints?
Persistent E0382 → rust-resource: Should use Arc/Rc for sharing?
Persistent E0597 → rust-type-driven: Are scope boundaries correct?
E0506/E0507 → rust-mutability: Should use interior mutability?
Trace Down (Implementation)
From design decisions to implementation:
"Data needs immutable sharing"
↓ Multi-threaded: Arc<T>
↓ Single-threaded: Rc<T>
"Data needs exclusive ownership"
↓ Return owned value
"Data is temporary use only"
↓ Use references within scope
"Need to pass data between functions"
↓ Consider lifetimes or return owned
Review Checklist
When reviewing ownership-related code:
- Each value has a clear owner
- Borrows don't outlive the borrowed data
- Mutable and immutable borrows don't overlap
- Lifetime annotations accurately reflect data relationships
- Smart pointer choice matches threading requirements
-
.clone()is used intentionally, not to avoid compiler errors - Lifetime elision is leveraged where possible
- Complex lifetime scenarios are documented
Verification Commands
# Check compilation
cargo check
# Run tests
cargo test
# Check for common mistakes
cargo clippy -- -W clippy::clone_on_copy -W clippy::unnecessary_clone
# Verify no memory leaks in tests
cargo test --features leak-check
Common Pitfalls
1. Clone Abuse
Symptom: .clone() everywhere to satisfy compiler
Fix: Understand actual ownership requirements, use references where possible
2. Lifetime Overuse
Symptom: Complex lifetime annotations everywhere
Fix: Return owned types, use smart pointers
3. Fighting Borrow Checker
Symptom: Constantly rewriting to satisfy compiler
Fix: Step back and redesign data flow
Related Skills
- rust-mutability - Interior mutability patterns (Cell, RefCell)
- rust-concurrency - Send/Sync and thread safety
- rust-unsafe - Raw pointers and manual memory management
- rust-lifetime-complex - Advanced lifetime patterns (HRTB, GAT)
- rust-resource - Resource management and RAII patterns
- rust-type-driven - Type-driven design
Localized Reference
- Chinese version: SKILL_ZH.md - 完整中文版本,包含所有内容