Add: windows mvp - transparent bugs not fixed
This commit is contained in:
177
skills/m14-mental-model/SKILL.md
Normal file
177
skills/m14-mental-model/SKILL.md
Normal file
@@ -0,0 +1,177 @@
|
||||
---
|
||||
name: m14-mental-model
|
||||
description: "Use when learning Rust concepts. Keywords: mental model, how to think about ownership, understanding borrow checker, visualizing memory layout, analogy, misconception, explaining ownership, why does Rust, help me understand, confused about, learning Rust, explain like I'm, ELI5, intuition for, coming from Java, coming from Python, 心智模型, 如何理解所有权, 学习 Rust, Rust 入门, 为什么 Rust"
|
||||
user-invocable: false
|
||||
---
|
||||
|
||||
# Mental Models
|
||||
|
||||
> **Layer 2: Design Choices**
|
||||
|
||||
## Core Question
|
||||
|
||||
**What's the right way to think about this Rust concept?**
|
||||
|
||||
When learning or explaining Rust:
|
||||
- What's the correct mental model?
|
||||
- What misconceptions should be avoided?
|
||||
- What analogies help understanding?
|
||||
|
||||
---
|
||||
|
||||
## Key Mental Models
|
||||
|
||||
| Concept | Mental Model | Analogy |
|
||||
|---------|--------------|---------|
|
||||
| Ownership | Unique key | Only one person has the house key |
|
||||
| Move | Key handover | Giving away your key |
|
||||
| `&T` | Lending for reading | Lending a book |
|
||||
| `&mut T` | Exclusive editing | Only you can edit the doc |
|
||||
| Lifetime `'a` | Valid scope | "Ticket valid until..." |
|
||||
| `Box<T>` | Heap pointer | Remote control to TV |
|
||||
| `Rc<T>` | Shared ownership | Multiple remotes, last turns off |
|
||||
| `Arc<T>` | Thread-safe Rc | Remotes from any room |
|
||||
|
||||
---
|
||||
|
||||
## Coming From Other Languages
|
||||
|
||||
| From | Key Shift |
|
||||
|------|-----------|
|
||||
| Java/C# | Values are owned, not references by default |
|
||||
| C/C++ | Compiler enforces safety rules |
|
||||
| Python/Go | No GC, deterministic destruction |
|
||||
| Functional | Mutability is safe via ownership |
|
||||
| JavaScript | No null, use Option instead |
|
||||
|
||||
---
|
||||
|
||||
## Thinking Prompt
|
||||
|
||||
When confused about Rust:
|
||||
|
||||
1. **What's the ownership model?**
|
||||
- Who owns this data?
|
||||
- How long does it live?
|
||||
- Who can access it?
|
||||
|
||||
2. **What guarantee is Rust providing?**
|
||||
- No data races
|
||||
- No dangling pointers
|
||||
- No use-after-free
|
||||
|
||||
3. **What's the compiler telling me?**
|
||||
- Error = violation of safety rule
|
||||
- Solution = work with the rules
|
||||
|
||||
---
|
||||
|
||||
## Trace Up ↑
|
||||
|
||||
To design understanding (Layer 2):
|
||||
|
||||
```
|
||||
"Why can't I do X in Rust?"
|
||||
↑ Ask: What safety guarantee would be violated?
|
||||
↑ Check: m01-m07 for the rule being enforced
|
||||
↑ Ask: What's the intended design pattern?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Trace Down ↓
|
||||
|
||||
To implementation (Layer 1):
|
||||
|
||||
```
|
||||
"I understand the concept, now how do I implement?"
|
||||
↓ m01-ownership: Ownership patterns
|
||||
↓ m02-resource: Smart pointer choice
|
||||
↓ m07-concurrency: Thread safety
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
| Error | Wrong Model | Correct Model |
|
||||
|-------|-------------|---------------|
|
||||
| E0382 use after move | GC cleans up | Ownership = unique key transfer |
|
||||
| E0502 borrow conflict | Multiple writers OK | Only one writer at a time |
|
||||
| E0499 multiple mut borrows | Aliased mutation | Exclusive access for mutation |
|
||||
| E0106 missing lifetime | Ignoring scope | References have validity scope |
|
||||
| E0507 cannot move from `&T` | Implicit clone | References don't own data |
|
||||
|
||||
## Deprecated Thinking
|
||||
|
||||
| Deprecated | Better |
|
||||
|------------|--------|
|
||||
| "Rust is like C++" | Different ownership model |
|
||||
| "Lifetimes are GC" | Compile-time validity scope |
|
||||
| "Clone solves everything" | Restructure ownership |
|
||||
| "Fight the borrow checker" | Work with the compiler |
|
||||
| "`unsafe` to avoid rules" | Understand safe patterns first |
|
||||
|
||||
---
|
||||
|
||||
## Ownership Visualization
|
||||
|
||||
```
|
||||
Stack Heap
|
||||
+----------------+ +----------------+
|
||||
| main() | | |
|
||||
| s1 ─────────────────────> │ "hello" |
|
||||
| | | |
|
||||
| fn takes(s) { | | |
|
||||
| s2 (moved) ─────────────> │ "hello" |
|
||||
| } | | (s1 invalid) |
|
||||
+----------------+ +----------------+
|
||||
|
||||
After move: s1 is no longer valid
|
||||
```
|
||||
|
||||
## Reference Visualization
|
||||
|
||||
```
|
||||
+----------------+
|
||||
| data: String |────────────> "hello"
|
||||
+----------------+
|
||||
↑
|
||||
│ &data (immutable borrow)
|
||||
│
|
||||
+------+------+
|
||||
| reader1 reader2 (multiple OK)
|
||||
+------+------+
|
||||
|
||||
+----------------+
|
||||
| data: String |────────────> "hello"
|
||||
+----------------+
|
||||
↑
|
||||
│ &mut data (mutable borrow)
|
||||
│
|
||||
+------+
|
||||
| writer (only one)
|
||||
+------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Learning Path
|
||||
|
||||
| Stage | Focus | Skills |
|
||||
|-------|-------|--------|
|
||||
| Beginner | Ownership basics | m01-ownership, m14-mental-model |
|
||||
| Intermediate | Smart pointers, error handling | m02, m06 |
|
||||
| Advanced | Concurrency, unsafe | m07, unsafe-checker |
|
||||
| Expert | Design patterns | m09-m15, domain-* |
|
||||
|
||||
---
|
||||
|
||||
## Related Skills
|
||||
|
||||
| When | See |
|
||||
|------|-----|
|
||||
| Ownership errors | m01-ownership |
|
||||
| Smart pointers | m02-resource |
|
||||
| Concurrency | m07-concurrency |
|
||||
| Anti-patterns | m15-anti-pattern |
|
||||
286
skills/m14-mental-model/patterns/thinking-in-rust.md
Normal file
286
skills/m14-mental-model/patterns/thinking-in-rust.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# 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 mutable references?**
|
||||
- Scope the mutations, use interior mutability, or redesign
|
||||
|
||||
3. **Does a reference outlive its source?**
|
||||
- Return owned data instead, or use `'static`
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### The Clone Escape Hatch
|
||||
|
||||
When fighting the borrow checker, `.clone()` often works:
|
||||
|
||||
```rust
|
||||
// Can't do this - double borrow
|
||||
let mut map = HashMap::new();
|
||||
for key in map.keys() {
|
||||
map.insert(key.clone(), process(key)); // ERROR: map borrowed twice
|
||||
}
|
||||
|
||||
// Clone to escape
|
||||
let keys: Vec<_> = map.keys().cloned().collect();
|
||||
for key in keys {
|
||||
map.insert(key.clone(), process(&key)); // OK
|
||||
}
|
||||
```
|
||||
|
||||
But ask: "Is there a better design?" Often, restructuring is better than cloning.
|
||||
|
||||
### The "Make It Own" Pattern
|
||||
|
||||
When lifetimes get complex, make the struct own its data:
|
||||
|
||||
```rust
|
||||
// Complex: struct with references
|
||||
struct Parser<'a> {
|
||||
input: &'a str,
|
||||
current: &'a str,
|
||||
}
|
||||
|
||||
// Simpler: struct owns data
|
||||
struct Parser {
|
||||
input: String,
|
||||
position: usize,
|
||||
}
|
||||
```
|
||||
|
||||
### The "Split the Borrow" Pattern
|
||||
|
||||
```rust
|
||||
struct Data {
|
||||
field_a: Vec<i32>,
|
||||
field_b: Vec<i32>,
|
||||
}
|
||||
|
||||
// Can't borrow self mutably twice
|
||||
fn process(&mut self) {
|
||||
// for a in &self.field_a {
|
||||
// self.field_b.push(*a); // ERROR
|
||||
// }
|
||||
|
||||
// Split the borrow
|
||||
let Data { field_a, field_b } = self;
|
||||
for a in field_a.iter() {
|
||||
field_b.push(*a); // OK: separate borrows
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The Rust Way
|
||||
|
||||
### Embrace the Type System
|
||||
|
||||
```rust
|
||||
// Don't: stringly-typed
|
||||
fn connect(host: &str, port: &str) { ... }
|
||||
connect("8080", "localhost"); // oops, wrong order
|
||||
|
||||
// Do: strongly-typed
|
||||
struct Host(String);
|
||||
struct Port(u16);
|
||||
fn connect(host: Host, port: Port) { ... }
|
||||
// connect(Port(8080), Host("localhost".into())); // compile error!
|
||||
```
|
||||
|
||||
### Make Invalid States Unrepresentable
|
||||
|
||||
```rust
|
||||
// Don't: runtime checks
|
||||
struct Connection {
|
||||
socket: Option<Socket>,
|
||||
connected: bool,
|
||||
}
|
||||
|
||||
// Do: types enforce states
|
||||
enum Connection {
|
||||
Disconnected,
|
||||
Connected { socket: Socket },
|
||||
}
|
||||
```
|
||||
|
||||
### Let the Compiler Guide You
|
||||
|
||||
```rust
|
||||
// Start with what you want
|
||||
fn process(data: ???) -> ???
|
||||
|
||||
// Let compiler errors tell you:
|
||||
// - What types are needed
|
||||
// - What lifetimes are needed
|
||||
// - What bounds are needed
|
||||
|
||||
// The error messages are documentation!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary: The Rust Mental Model
|
||||
|
||||
1. **Values have owners** - exactly one at a time
|
||||
2. **Borrowing is lending** - temporary access, owner retains responsibility
|
||||
3. **Lifetimes are scopes** - compiler tracks validity
|
||||
4. **Types encode constraints** - use them to prevent bugs
|
||||
5. **The compiler is your friend** - work with it, not against it
|
||||
|
||||
When stuck:
|
||||
- Clone to make progress
|
||||
- Restructure to own instead of borrow
|
||||
- Ask: "What is the compiler trying to tell me?"
|
||||
Reference in New Issue
Block a user