Rust-skills rust-ownership

Ownership, borrowing, and lifetime expert. Handles compiler errors E0382, E0597, E0506, E0507, E0515, E0716, E0106 and provides systematic solutions for memory safety patterns.

install
source · Clone the upstream repo
git clone https://github.com/huiali/rust-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/huiali/rust-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.codex/skills/rust-ownership" ~/.claude/skills/huiali-rust-skills-rust-ownership && rm -rf "$T"
manifest: .codex/skills/rust-ownership/SKILL.md
source content

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
  • s2
    is 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:

  1. Clarify each reference's lifetime
  2. Use named lifetimes to express relationships
  3. Prefer returning owned types

Workflow

Step 1: Who Owns the Data?

SituationOwner
Function parameterCaller owns
Function-local variableFunction owns (destroyed on return)
Struct fieldStruct instance owns
Arc<T>
Multiple shared owners

Step 2: Is Borrowing Appropriate?

OperationBorrow TypeNotes
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

ScenarioChoiceReason
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-PatternProblemCorrect Approach
.clone()
everywhere
Hides ownership issuesThink about actual ownership needs
'static
for everything
Too loose and impreciseUse actual required lifetimes
Box::leak()
memory leaks
Memory wasteUse proper lifetime management
Fighting the borrow checkerDigging your own holeUnderstand 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

CodeMeaningDon't SayAsk Instead
E0382Use of moved value"clone it"Who should own this data?
E0597Lifetime too short"extend lifetime"Are scope boundaries correct?
E0506Borrow not ended before mutation"end borrow first"Where should mutation occur?
E0507Move out of reference"clone before move"Why move from reference?
E0515Return non-owned data"return owned"Should caller own the data?
E0716Temporary value lifetime insufficient"bind to variable"Why is this temporary?
E0106Missing 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 - 完整中文版本,包含所有内容