
M01 Ownership
- 1.5k installs
- 1.3k repo stars
- Updated May 24, 2026
- actionbook/rust-skills
m01-ownership is an agent skill for Rust ownership, borrow, and lifetime errors with design-first questioning.
About
The m01-ownership skill addresses Rust ownership, borrow, and lifetime issues including errors E0382 E0597 E0506 E0507 E0515 E0716 E0106 with design-first questioning instead of reflexive clones. Core question asks who should own data and for how long considering shared versus exclusive, short-lived versus long-lived, and transformed versus read-only roles. Error tables map each compiler error to design questions like who should own versus extend lifetime. Thinking prompts distinguish entity value object and temporary data roles and intentional versus accidental ownership. Trace up escalates repeated E0382 to m02-resource Arc Rc and E0597 to m09-domain scope boundaries. Use for ownership, borrow, lifetime, move, and clone decisions in Rust.
- Core question: who owns data and for how long?
- Error map: E0382 E0597 E0506 E0507 E0515 E0716 E0106.
- Design-first: ask who should own before cloning.
- Entity vs Value Object vs temporary data roles.
- Trace up to m02-resource m03-mutability m09-domain.
M01 Ownership by the numbers
- 1,456 all-time installs (skills.sh)
- +52 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #325 of 4,386 Backend & APIs skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
m01-ownership capabilities & compatibility
- Capabilities
- error to design questioning · ownership role analysis · sibling skill trace routing · escalation after repeated failures
- Use cases
- code review
What m01-ownership says it does
Who should own this data, and for how long?
E0382 | "Clone it" | Who should own this data?
npx skills add https://github.com/actionbook/rust-skills --skill m01-ownershipAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 1.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | May 24, 2026 |
| Repository | actionbook/rust-skills ↗ |
How do I fix E0382 or E0597 ownership errors without blindly cloning everything?
Resolve Rust ownership, borrow, and lifetime errors E0382 E0597 with design-first questioning.
Who is it for?
Rust developers hitting move, borrow, and lifetime compiler errors.
Skip if: Skip for smart pointer selection covered by m02-resource or trait generics in m04-zero-cost.
When should I use this skill?
User hits ownership borrow lifetime errors E0382 E0597 move clone value moved.
What you get
An ownership design answer matching data role, scope, and borrow structure not just compiler silence.
- Ownership comparison tables
- Move-semantics code snippets
Files
Ownership & Lifetimes
Layer 1: Language Mechanics
Core Question
Who should own this data, and for how long?
Before fixing ownership errors, understand the data's role:
- Is it shared or exclusive?
- Is it short-lived or long-lived?
- Is it transformed or just read?
---
Error → Design Question
| Error | Don't Just Say | Ask Instead |
|---|---|---|
| E0382 | "Clone it" | Who should own this data? |
| E0597 | "Extend lifetime" | Is the scope boundary correct? |
| E0506 | "End borrow first" | Should mutation happen elsewhere? |
| E0507 | "Clone before move" | Why are we moving from a reference? |
| E0515 | "Return owned" | Should caller own the data? |
| E0716 | "Bind to variable" | Why is this temporary? |
| E0106 | "Add 'a" | What is the actual lifetime relationship? |
---
Thinking Prompt
Before fixing an ownership error, ask:
1. What is this data's domain role?
- Entity (unique identity) → owned
- Value Object (interchangeable) → clone/copy OK
- Temporary (computation result) → maybe restructure
2. Is the ownership design intentional?
- By design → work within constraints
- Accidental → consider redesign
3. Fix symptom or redesign?
- If Strike 3 (3rd attempt) → escalate to Layer 2
---
Trace Up ↑
When errors persist, trace to design layer:
E0382 (moved value)
↑ Ask: What design choice led to this ownership pattern?
↑ Check: m09-domain (is this Entity or Value Object?)
↑ Check: domain-* (what constraints apply?)| Persistent Error | Trace To | Question |
|---|---|---|
| E0382 repeated | m02-resource | Should use Arc/Rc for sharing? |
| E0597 repeated | m09-domain | Is scope boundary at right place? |
| E0506/E0507 | m03-mutability | Should use interior mutability? |
---
Trace Down ↓
From design decisions to implementation:
"Data needs to be shared immutably"
↓ Use: Arc<T> (multi-thread) or Rc<T> (single-thread)
"Data needs exclusive ownership"
↓ Use: move semantics, take ownership
"Data is read-only view"
↓ Use: &T (immutable borrow)---
Quick Reference
| Pattern | Ownership | Cost | Use When |
|---|---|---|---|
| Move | Transfer | Zero | Caller doesn't need data |
&T | Borrow | Zero | Read-only access |
&mut T | Exclusive borrow | Zero | Need to modify |
clone() | Duplicate | Alloc + copy | Actually need a copy |
Rc<T> | Shared (single) | Ref count | Single-thread sharing |
Arc<T> | Shared (multi) | Atomic ref count | Multi-thread sharing |
Cow<T> | Clone-on-write | Alloc if mutated | Might modify |
Error Code Reference
| Error | Cause | Quick Fix |
|---|---|---|
| E0382 | Value moved | Clone, reference, or redesign ownership |
| E0597 | Reference outlives owner | Extend owner scope or restructure |
| E0506 | Assign while borrowed | End borrow before mutation |
| E0507 | Move out of borrowed | Clone or use reference |
| E0515 | Return local reference | Return owned value |
| E0716 | Temporary dropped | Bind to variable |
| E0106 | Missing lifetime | Add 'a annotation |
---
Anti-Patterns
| Anti-Pattern | Why Bad | Better |
|---|---|---|
.clone() everywhere | Hides design issues | Design ownership properly |
| Fight borrow checker | Increases complexity | Work with the compiler |
'static for everything | Restricts flexibility | Use appropriate lifetimes |
Leak with Box::leak | Memory leak | Proper lifetime design |
---
Related Skills
| When | See |
|---|---|
| Need smart pointers | m02-resource |
| Need interior mutability | m03-mutability |
| Data is domain entity | m09-domain |
| Learning ownership concepts | m14-mental-model |
Ownership: Comparison with Other Languages
Rust vs C++
Memory Management
| Aspect | Rust | C++ |
|---|---|---|
| Default | Move semantics | Copy semantics (pre-C++11) |
| Move | let b = a; (a invalidated) | auto b = std::move(a); (a valid but unspecified) |
| Copy | let b = a.clone(); | auto b = a; |
| Safety | Compile-time enforcement | Runtime responsibility |
Rust Move vs C++ Move
// Rust: after move, 'a' is INVALID
let a = String::from("hello");
let b = a; // a moved
// println!("{}", a); // COMPILE ERROR
// Equivalent in C++:
// std::string a = "hello";
// std::string b = std::move(a);
// std::cout << a; // UNDEFINED (compiles but buggy)Smart Pointers
| Rust | C++ | Purpose |
|---|---|---|
Box<T> | std::unique_ptr<T> | Unique ownership |
Rc<T> | std::shared_ptr<T> | Shared ownership |
Arc<T> | std::shared_ptr<T> + atomic | Thread-safe shared |
RefCell<T> | (manual runtime checks) | Interior mutability |
---
Rust vs Go
Memory Model
| Aspect | Rust | Go |
|---|---|---|
| Memory | Stack + heap, explicit | GC manages all |
| Ownership | Enforced at compile-time | None (GC handles) |
| Null | Option<T> | nil for pointers |
| Concurrency | Send/Sync traits | Channels (less strict) |
Sharing Data
// Rust: explicit about sharing
use std::sync::Arc;
let data = Arc::new(vec![1, 2, 3]);
let data_clone = Arc::clone(&data);
std::thread::spawn(move || {
println!("{:?}", data_clone);
});
// Go: implicit sharing
// data := []int{1, 2, 3}
// go func() {
// fmt.Println(data) // potential race condition
// }()Why No GC in Rust
1. Deterministic destruction: Resources freed exactly when scope ends 2. Zero-cost: No GC pauses or overhead 3. Embeddable: Works in OS kernels, embedded systems 4. Predictable latency: Critical for real-time systems
---
Rust vs Java/C#
Reference Semantics
| Aspect | Rust | Java/C# |
|---|---|---|
| Objects | Owned by default | Reference by default |
| Null | Option<T> | null (nullable) |
| Immutability | Default | Must use final/readonly |
| Copy | Explicit .clone() | Reference copy (shallow) |
Comparison
// Rust: clear ownership
fn process(data: Vec<i32>) { // takes ownership
// data is ours, will be freed at end
}
let numbers = vec![1, 2, 3];
process(numbers);
// numbers is invalid here
// Java: ambiguous ownership
// void process(List<Integer> data) {
// // Who owns data? Caller? Callee? Both?
// // Can caller still use it?
// }---
Rust vs Python
Memory Model
| Aspect | Rust | Python |
|---|---|---|
| Typing | Static, compile-time | Dynamic, runtime |
| Memory | Ownership-based | Reference counting + GC |
| Mutability | Default immutable | Default mutable |
| Performance | Native, zero-cost | Interpreted, higher overhead |
Common Pattern Translation
// Rust: borrowing iteration
let items = vec!["a", "b", "c"];
for item in &items {
println!("{}", item);
}
// items still usable
// Python: iteration doesn't consume
// items = ["a", "b", "c"]
// for item in items:
// print(item)
// items still usable (different reason - ref counting)---
Unique Rust Concepts
Concepts Other Languages Lack
1. Borrow Checker: No other mainstream language has compile-time borrow checking 2. Lifetimes: Explicit annotation of reference validity 3. Move by Default: Values move, not copy 4. No Null: Option<T> instead of null pointers 5. Affine Types: Values can be used at most once
Learning Curve Areas
| Concept | Coming From | Key Insight |
|---|---|---|
| Ownership | GC languages | Think about who "owns" data |
| Borrowing | C/C++ | Like references but checked |
| Lifetimes | Any | Explicit scope of validity |
| Move | C++ | Move is default, not copy |
---
Mental Model Shifts
From GC Languages (Java, Go, Python)
Before: "Memory just works, GC handles it"
After: "I explicitly decide who owns data and when it's freed"Key shifts:
- Think about ownership at design time
- Returning references requires lifetime thinking
- No more
null- useOption<T>
From C/C++
Before: "I manually manage memory and hope I get it right"
After: "Compiler enforces correctness, I fight the borrow checker"Key shifts:
- Trust the compiler's errors
- Move is the default (unlike C++ copy)
- Smart pointers are idiomatic, not overhead
From Functional Languages (Haskell, ML)
Before: "Everything is immutable, copying is fine"
After: "Mutability is explicit, ownership prevents aliasing"Key shifts:
- Mutability is safe because of ownership rules
- No persistent data structures needed (usually)
- Performance characteristics are explicit
---
Performance Trade-offs
| Language | Memory Overhead | Latency | Throughput |
|---|---|---|---|
| Rust | Minimal (no GC) | Predictable | Excellent |
| C++ | Minimal | Predictable | Excellent |
| Go | GC overhead | GC pauses | Good |
| Java | GC overhead | GC pauses | Good |
| Python | High (ref counting + GC) | Variable | Lower |
When Rust Ownership Wins
1. Real-time systems: No GC pauses 2. Embedded: No runtime overhead 3. High-performance: Zero-cost abstractions 4. Concurrent: Data races prevented at compile time
When GC Might Be Preferable
1. Rapid prototyping: Less mental overhead 2. Complex object graphs: Cycles are tricky in Rust 3. GUI applications: Object lifetimes are dynamic 4. Small programs: Overhead doesn't matter
Ownership Best Practices
API Design Patterns
1. Prefer Borrowing Over Ownership
// BAD: takes ownership unnecessarily
fn print_name(name: String) {
println!("Name: {}", name);
}
// GOOD: borrows instead
fn print_name(name: &str) {
println!("Name: {}", name);
}
// Caller benefits:
let name = String::from("Alice");
print_name(&name); // can reuse name
print_name(&name); // still valid2. Return Owned Values from Constructors
// GOOD: return owned value
impl User {
fn new(name: &str) -> Self {
User {
name: name.to_string(),
}
}
}
// GOOD: accept Into<String> for flexibility
impl User {
fn new(name: impl Into<String>) -> Self {
User {
name: name.into(),
}
}
}
// Usage:
let u1 = User::new("Alice"); // &str
let u2 = User::new(String::from("Bob")); // String3. Use AsRef for Generic Borrowing
// GOOD: accepts both &str and String
fn process<S: AsRef<str>>(input: S) {
let s = input.as_ref();
println!("{}", s);
}
process("literal"); // &str
process(String::from("owned")); // String
process(&String::from("ref")); // &String4. Cow for Clone-on-Write
use std::borrow::Cow;
// Return borrowed when possible, owned when needed
fn maybe_modify(s: &str, uppercase: bool) -> Cow<'_, str> {
if uppercase {
Cow::Owned(s.to_uppercase()) // allocates
} else {
Cow::Borrowed(s) // zero-cost
}
}
let input = "hello";
let result = maybe_modify(input, false);
// result is borrowed, no allocation---
Struct Design Patterns
1. Owned Fields vs References
// Use owned fields for most cases
struct User {
name: String,
email: String,
}
// Use references only when lifetime is clear
struct UserView<'a> {
name: &'a str,
email: &'a str,
}
// Pattern: owned data + view for efficiency
impl User {
fn view(&self) -> UserView<'_> {
UserView {
name: &self.name,
email: &self.email,
}
}
}2. Builder Pattern with Ownership
#[derive(Default)]
struct RequestBuilder {
url: Option<String>,
method: Option<String>,
body: Option<Vec<u8>>,
}
impl RequestBuilder {
fn new() -> Self {
Self::default()
}
// Take self by value for chaining
fn url(mut self, url: impl Into<String>) -> Self {
self.url = Some(url.into());
self
}
fn method(mut self, method: impl Into<String>) -> Self {
self.method = Some(method.into());
self
}
fn build(self) -> Result<Request, Error> {
Ok(Request {
url: self.url.ok_or(Error::MissingUrl)?,
method: self.method.unwrap_or_else(|| "GET".to_string()),
body: self.body.unwrap_or_default(),
})
}
}
// Usage:
let req = RequestBuilder::new()
.url("https://example.com")
.method("POST")
.build()?;3. Interior Mutability When Needed
use std::cell::RefCell;
use std::rc::Rc;
// Shared mutable state in single-threaded context
struct Counter {
value: Rc<RefCell<u32>>,
}
impl Counter {
fn new() -> Self {
Counter {
value: Rc::new(RefCell::new(0)),
}
}
fn increment(&self) {
*self.value.borrow_mut() += 1;
}
fn get(&self) -> u32 {
*self.value.borrow()
}
fn clone_handle(&self) -> Self {
Counter {
value: Rc::clone(&self.value),
}
}
}---
Collection Patterns
1. Efficient Iteration
let items = vec![1, 2, 3, 4, 5];
// Iterate by reference (no move)
for item in &items {
println!("{}", item);
}
// Iterate by mutable reference
for item in &mut items.clone() {
*item *= 2;
}
// Consume with into_iter when done
let sum: i32 = items.into_iter().sum();2. Collecting Results
// Collect into owned collection
let strings: Vec<String> = (0..5)
.map(|i| format!("item_{}", i))
.collect();
// Collect references
let refs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
// Collect with transformation
let result: Result<Vec<i32>, _> = ["1", "2", "3"]
.iter()
.map(|s| s.parse::<i32>())
.collect();3. Entry API for Maps
use std::collections::HashMap;
let mut map: HashMap<String, Vec<i32>> = HashMap::new();
// Efficient: don't search twice
map.entry("key".to_string())
.or_insert_with(Vec::new)
.push(42);
// With entry modification
map.entry("key".to_string())
.and_modify(|v| v.push(43))
.or_insert_with(|| vec![43]);---
Error Handling with Ownership
1. Preserve Context in Errors
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct ParseError {
input: String, // owns the problematic input
message: String,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Failed to parse '{}': {}", self.input, self.message)
}
}
fn parse(input: &str) -> Result<i32, ParseError> {
input.parse().map_err(|_| ParseError {
input: input.to_string(), // clone for error context
message: "not a valid integer".to_string(),
})
}2. Ownership in Result Chains
fn process_data(path: &str) -> Result<ProcessedData, Error> {
let content = std::fs::read_to_string(path)?; // owned String
let parsed = parse_content(&content)?; // borrow
let processed = transform(parsed)?; // ownership moves
Ok(processed) // return owned
}---
Performance Considerations
1. Avoid Unnecessary Clones
// BAD: cloning just to compare
fn contains_item(items: &[String], target: &str) -> bool {
items.iter().any(|s| s.clone() == target) // unnecessary clone
}
// GOOD: compare references
fn contains_item(items: &[String], target: &str) -> bool {
items.iter().any(|s| s == target) // String implements PartialEq<str>
}2. Use Slices for Flexibility
// BAD: requires Vec
fn sum(numbers: &Vec<i32>) -> i32 {
numbers.iter().sum()
}
// GOOD: accepts any slice
fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
// Now works with:
sum(&vec![1, 2, 3]); // Vec
sum(&[1, 2, 3]); // array
sum(&array[1..3]); // slice3. In-Place Mutation
// BAD: allocates new String
fn make_uppercase(s: &str) -> String {
s.to_uppercase()
}
// GOOD when you own the data: mutate in place
fn make_uppercase(mut s: String) -> String {
s.make_ascii_uppercase(); // in-place for ASCII
s
}Common Ownership Errors & Fixes
E0382: Use of Moved Value
Error Pattern
let s = String::from("hello");
let s2 = s; // s moved here
println!("{}", s); // ERROR: value borrowed after moveFix Options
Option 1: Clone (if ownership not needed)
let s = String::from("hello");
let s2 = s.clone(); // s is cloned
println!("{}", s); // OK: s still validOption 2: Borrow (if modification not needed)
let s = String::from("hello");
let s2 = &s; // borrow, not move
println!("{}", s); // OK
println!("{}", s2); // OKOption 3: Use Rc/Arc (for shared ownership)
use std::rc::Rc;
let s = Rc::new(String::from("hello"));
let s2 = Rc::clone(&s); // shared ownership
println!("{}", s); // OK
println!("{}", s2); // OK---
E0597: Borrowed Value Does Not Live Long Enough
Error Pattern
fn get_str() -> &str {
let s = String::from("hello");
&s // ERROR: s dropped here, but reference returned
}Fix Options
Option 1: Return owned value
fn get_str() -> String {
String::from("hello") // return owned value
}Option 2: Use 'static lifetime
fn get_str() -> &'static str {
"hello" // string literal has 'static lifetime
}Option 3: Accept reference parameter
fn get_str<'a>(s: &'a str) -> &'a str {
s // return reference with same lifetime as input
}---
E0499: Cannot Borrow as Mutable More Than Once
Error Pattern
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // ERROR: second mutable borrow
println!("{}, {}", r1, r2);Fix Options
Option 1: Sequential borrows
let mut s = String::from("hello");
{
let r1 = &mut s;
r1.push_str(" world");
} // r1 goes out of scope
let r2 = &mut s; // OK: r1 no longer existsOption 2: Use RefCell for interior mutability
use std::cell::RefCell;
let s = RefCell::new(String::from("hello"));
let mut r1 = s.borrow_mut();
// drop r1 before borrowing again
drop(r1);
let mut r2 = s.borrow_mut();---
E0502: Cannot Borrow as Mutable While Immutable Borrow Exists
Error Pattern
let mut v = vec![1, 2, 3];
let first = &v[0]; // immutable borrow
v.push(4); // ERROR: mutable borrow while immutable exists
println!("{}", first);Fix Options
Option 1: Finish using immutable borrow first
let mut v = vec![1, 2, 3];
let first = v[0]; // copy value, not borrow
v.push(4); // OK
println!("{}", first); // OK: using copied valueOption 2: Clone before mutating
let mut v = vec![1, 2, 3];
let first = v[0].clone(); // if T: Clone
v.push(4);
println!("{}", first);---
E0507: Cannot Move Out of Borrowed Content
Error Pattern
fn take_string(s: &String) {
let moved = *s; // ERROR: cannot move out of borrowed content
}Fix Options
Option 1: Clone
fn take_string(s: &String) {
let cloned = s.clone();
}Option 2: Take ownership in function signature
fn take_string(s: String) { // take ownership
let moved = s;
}Option 3: Use mem::take for Option/Default types
fn take_from_option(opt: &mut Option<String>) -> Option<String> {
std::mem::take(opt) // replaces with None, returns owned value
}---
E0515: Return Local Reference
Error Pattern
fn create_string() -> &String {
let s = String::from("hello");
&s // ERROR: cannot return reference to local variable
}Fix Options
Option 1: Return owned value
fn create_string() -> String {
String::from("hello")
}Option 2: Use static/const
fn get_static_str() -> &'static str {
"hello"
}---
E0716: Temporary Value Dropped While Borrowed
Error Pattern
let r: &str = &String::from("hello"); // ERROR: temporary dropped
println!("{}", r);Fix Options
Option 1: Bind to variable first
let s = String::from("hello");
let r: &str = &s;
println!("{}", r);Option 2: Use let binding with reference
let r: &str = {
let s = String::from("hello");
// s.as_str() // ERROR: still temporary
Box::leak(s.into_boxed_str()) // extreme: leak for 'static
};---
Pattern: Loop Ownership Issues
Error Pattern
let strings = vec![String::from("a"), String::from("b")];
for s in strings {
println!("{}", s);
}
// ERROR: strings moved into loop
println!("{:?}", strings);Fix Options
Option 1: Iterate by reference
let strings = vec![String::from("a"), String::from("b")];
for s in &strings {
println!("{}", s);
}
println!("{:?}", strings); // OKOption 2: Use iter()
for s in strings.iter() {
println!("{}", s);
}Option 3: Clone if needed
for s in strings.clone() {
// consumes cloned vec
}
println!("{:?}", strings); // original still availableLifetime Patterns
Basic Lifetime Annotation
When Required
// ERROR: missing lifetime specifier
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
// FIX: explicit lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}Lifetime Elision Rules
1. Each input reference gets its own lifetime 2. If one input lifetime, output uses same 3. If &self or &mut self, output uses self's lifetime
// These are equivalent (elision applies):
fn first_word(s: &str) -> &str { ... }
fn first_word<'a>(s: &'a str) -> &'a str { ... }
// Method with self (elision applies):
impl MyStruct {
fn get_ref(&self) -> &str { ... }
// Equivalent to:
fn get_ref<'a>(&'a self) -> &'a str { ... }
}---
Struct Lifetimes
Struct Holding References
// Struct must declare lifetime for references
struct Excerpt<'a> {
part: &'a str,
}
impl<'a> Excerpt<'a> {
fn level(&self) -> i32 { 3 }
// Return reference tied to self's lifetime
fn get_part(&self) -> &str {
self.part
}
}Multiple Lifetimes in Struct
struct Multi<'a, 'b> {
x: &'a str,
y: &'b str,
}
// Use when references may have different lifetimes
fn make_multi<'a, 'b>(x: &'a str, y: &'b str) -> Multi<'a, 'b> {
Multi { x, y }
}---
'static Lifetime
When to Use
// String literals are 'static
let s: &'static str = "hello";
// Owned data can be leaked to 'static
let leaked: &'static str = Box::leak(String::from("hello").into_boxed_str());
// Thread spawn requires 'static or move
std::thread::spawn(move || {
// closure owns data, satisfies 'static
});Avoid Overusing 'static
// BAD: requires 'static unnecessarily
fn process(s: &'static str) { ... }
// GOOD: use generic lifetime
fn process<'a>(s: &'a str) { ... }
// or
fn process(s: &str) { ... } // lifetime elision---
Higher-Ranked Trait Bounds (HRTB)
for<'a> Syntax
// Function that works with any lifetime
fn apply_to_ref<F>(f: F)
where
F: for<'a> Fn(&'a str) -> &'a str,
{
let s = String::from("hello");
let result = f(&s);
println!("{}", result);
}Common Use: Closure Bounds
// Closure that borrows any lifetime
fn filter_refs<F>(items: &[&str], pred: F) -> Vec<&str>
where
F: for<'a> Fn(&'a str) -> bool,
{
items.iter().copied().filter(|s| pred(s)).collect()
}---
Lifetime Bounds
'a: 'b (Outlives)
// 'a must live at least as long as 'b
fn coerce<'a, 'b>(x: &'a str) -> &'b str
where
'a: 'b,
{
x
}T: 'a (Type Outlives Lifetime)
// T must live at least as long as 'a
struct Wrapper<'a, T: 'a> {
value: &'a T,
}
// Common pattern with trait objects
fn use_trait<'a, T: MyTrait + 'a>(t: &'a T) { ... }---
Common Lifetime Mistakes
Mistake 1: Returning Reference to Local
// WRONG
fn dangle() -> &String {
let s = String::from("hello");
&s // s dropped, reference invalid
}
// RIGHT
fn no_dangle() -> String {
String::from("hello")
}Mistake 2: Conflicting Lifetimes
// WRONG: might return reference to y which has shorter lifetime
fn wrong<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
y // ERROR: 'b might not live as long as 'a
}
// RIGHT: use same lifetime or add bound
fn right<'a>(x: &'a str, y: &'a str) -> &'a str {
y // OK: both have lifetime 'a
}Mistake 3: Struct Outlives Reference
// WRONG: s might outlive the string it references
let r;
{
let s = String::from("hello");
r = Excerpt { part: &s }; // ERROR
}
println!("{}", r.part); // s already dropped
// RIGHT: ensure source outlives struct
let s = String::from("hello");
let r = Excerpt { part: &s };
println!("{}", r.part); // OK: s still in scope---
Subtyping and Variance
Covariance
// &'a T is covariant in 'a
// Can use &'long where &'short expected
fn example<'short, 'long: 'short>(long_ref: &'long str) {
let short_ref: &'short str = long_ref; // OK: covariance
}Invariance
// &'a mut T is invariant in 'a
fn example<'a, 'b>(x: &'a mut &'b str, y: &'b str) {
*x = y; // ERROR if 'a and 'b are different
}Practical Impact
// This works due to covariance
fn accept_any<'a>(s: &'a str) { ... }
let s = String::from("hello");
let long_lived: &str = &s;
accept_any(long_lived); // 'long coerces to 'shortRelated skills
Forks & variants (1)
M01 Ownership has 1 known copy in the catalog totaling 822 installs. They canonicalize to this original listing.
- zhanghandong - 822 installs
How it compares
Pick m01-ownership before m02-resource when the blocker is basic move/borrow rules rather than choosing between Box, Rc, or Arc.
FAQ
Should I just clone on E0382?
Ask who should own the data first — clone only when Value Object semantics justify it.
When escalate beyond fixes?
On third failed attempt trace up to m02-resource for Arc/Rc or m09-domain for scope redesign.
Is m01-ownership safe to install?
Review the Security Audits panel on this page before installing in production.