
M14 Mental Model
Internalize ownership, borrowing, and lifetime mental models so your agent explains Rust errors and designs APIs in terms of resource responsibility—not pointer chasing.
Overview
m14-mental-model is a journey-wide agent skill that teaches ownership, borrowing, and lifetime mental models—usable whenever a solo builder needs to reason about Rust resource rules before committing to an API or fix.
Install
npx skills add https://github.com/actionbook/rust-skills --skill m14-mental-modelWhat is this skill?
- Reframes heap thinking as single-owner resource management with automatic drop at scope end
- Borrowing explained as temporary access with immutable and mutable rules and caller retention
- Lifetime annotations tied to “reference cannot outlive data” with longest-of-two-slices example
- Contrast section for developers coming from Java/C# reference-heavy mental models
- Three core mental models: ownership, borrowing, and lifetimes
Adoption & trust: 921 installs on skills.sh; 1.2k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You keep hitting borrow checker errors because you still think in shared references like Java or C# instead of single ownership and explicit lifetimes.
Who is it for?
Builders new to Rust or returning after a break who want conceptual framing before writing structs, traits, or async services.
Skip if: Experts who only need crate-specific API docs, or teams that want automated Clippy fixes without teaching underlying models.
When should I use this skill?
Whenever Rust ownership, borrowing, or lifetime errors do not match your prior GC/reference language intuition.
What do I get? / Deliverables
You and your agent share a consistent vocabulary for moves, borrows, and 'a lifetimes so designs and fixes align with how rustc models memory.
- Shared vocabulary for ownership/borrow/lifetime decisions
- Example-aligned explanations for agent-assisted refactors
Recommended Skills
Journey fit
Useful at every journey phase - explore requirements and options before committing to a direction.
Where it fits
Sketch a small Rust CLI prototype and decide whether functions should take &str or owned String before committing to a crate layout.
Design an HTTP handler API where request data is borrowed for the request lifetime instead of cloning strings unnecessarily.
Interpret compile errors about references outliving data when refactoring a module to return &str from internal buffers.
Patch a production Rust service safely by tracing which struct owns configuration loaded at startup.
Evaluate whether Rust’s ownership model fits a performance-sensitive side project compared with Go or TypeScript.
How it compares
Conceptual mental-model primer—not a Cargo project generator, test runner, or production observability skill.
Common Questions / FAQ
Who is m14-mental-model for?
Solo and indie developers learning Rust for CLIs, APIs, or systems components who need ownership explained in plain mental-model terms.
When should I use m14-mental-model?
During Build when designing backend APIs; during Validate when sketching a Rust prototype; during Ship review when untangling lifetime errors; and anytime before refactoring code that mixes &T, &mut T, and owned values.
Is m14-mental-model safe to install?
It is educational markdown without shell or network actions; still review the Security Audits panel on this Prism page for the upstream package.
SKILL.md
READMESKILL.md - M14 Mental Model
# Thinking in Rust: Mental Models ## Core Mental Models ### 1. Ownership as Resource Management ``` Traditional: "Who has a pointer to this data?" Rust: "Who OWNS this data and is responsible for freeing it?" ``` Key insight: Every value has exactly one owner. When the owner goes out of scope, the value is dropped. ```rust { let s = String::from("hello"); // s owns the String // use s... } // s goes out of scope, String is dropped (memory freed) ``` ### 2. Borrowing as Temporary Access ``` Traditional: "I'll just read from this pointer" Rust: "I'm borrowing this value, owner still responsible for it" ``` Key insight: Borrows are like library books - you can read them, but must return them. ```rust fn print_length(s: &String) { // borrows s println!("{}", s.len()); } // borrow ends, caller still owns s let my_string = String::from("hello"); print_length(&my_string); // lend to function println!("{}", my_string); // still have it ``` ### 3. Lifetimes as Validity Scopes ``` Traditional: "Hope this pointer is still valid" Rust: "Compiler tracks exactly how long references are valid" ``` Key insight: A reference can't outlive the data it points to. ```rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { // 'a means: the returned reference is valid as long as BOTH inputs are valid if x.len() > y.len() { x } else { y } } ``` --- ## Shifting Perspectives ### From "Everything is a Reference" (Java/C#) Java mental model: ```java // Everything is implicitly a reference User user = new User("Alice"); // user is a reference List<User> users = new ArrayList<>(); users.add(user); // shares the reference user.setName("Bob"); // affects the list too! ``` Rust mental model: ```rust // Values are owned, sharing is explicit let user = User::new("Alice"); // user is owned let mut users = vec![]; users.push(user); // user moved into vec, can't use user anymore // user.set_name("Bob"); // ERROR: user was moved // If you need sharing: use std::rc::Rc; let user = Rc::new(User::new("Alice")); let user2 = Rc::clone(&user); // explicit shared ownership ``` ### From "Manual Memory Management" (C/C++) C mental model: ```c char* s = malloc(100); // ... must remember to free(s) ... // ... what if we return early? ... // ... what if an exception occurs? ... free(s); ``` Rust mental model: ```rust let s = String::with_capacity(100); // ... use s ... // No need to free - Rust drops s automatically when scope ends // Even with early returns, panics, or any control flow ``` ### From "Garbage Collection" (Go/Python) GC mental model: ```python # Create objects, GC will figure it out users = [] for name in names: users.append(User(name)) # GC runs sometime later, when it feels like it ``` Rust mental model: ```rust let users: Vec<User> = names .iter() .map(|name| User::new(name)) .collect(); // Memory is freed EXACTLY when users goes out of scope // Deterministic, no GC pauses, no unpredictable memory usage ``` --- ## Key Questions to Ask ### When Designing Functions 1. **Does this function need to own the data, or just read it?** - Need to keep it: take ownership (`fn process(data: Vec<T>)`) - Just reading: borrow (`fn process(data: &[T])`) - Need to modify: mutable borrow (`fn process(data: &mut Vec<T>)`) 2. **Does the return value contain references to inputs?** - Yes: need lifetime annotations - No: lifetime elision usually works ### When Designing Structs 1. **Should this struct own its data or reference it?** - Long-lived, independent: own (`name: String`) - Short-lived view: reference (`name: &'a str`) 2. **Do multiple parts need to access the same data?** - Single-threaded: `Rc<T>` or `Rc<RefCell<T>>` - Multi-threaded: `Arc<T>` or `Arc<Mutex<T>>` ### When Hitting Borrow Checker Errors 1. **Am I trying to use a value after moving it?** - Clone it, borrow it, or restructure the code 2. **Am I trying to have multiple mutab