@booklib/skills 1.3.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +108 -0
- package/CLAUDE.md +58 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +13 -0
- package/README.md +69 -45
- package/SECURITY.md +9 -0
- package/assets/logo.svg +36 -0
- package/demo.gif +0 -0
- package/demo.tape +40 -0
- package/docs/index.html +187 -0
- package/package.json +2 -2
- package/skills/effective-typescript/SKILL.md +166 -0
- package/skills/effective-typescript/evals/evals.json +36 -0
- package/skills/effective-typescript/examples/after.md +70 -0
- package/skills/effective-typescript/examples/before.md +47 -0
- package/skills/effective-typescript/references/api_reference.md +118 -0
- package/skills/effective-typescript/references/practices-catalog.md +371 -0
- package/skills/programming-with-rust/SKILL.md +194 -0
- package/skills/programming-with-rust/evals/evals.json +37 -0
- package/skills/programming-with-rust/examples/after.md +107 -0
- package/skills/programming-with-rust/examples/before.md +59 -0
- package/skills/programming-with-rust/references/api_reference.md +152 -0
- package/skills/programming-with-rust/references/practices-catalog.md +335 -0
- package/skills/rust-in-action/SKILL.md +290 -0
- package/skills/rust-in-action/evals/evals.json +38 -0
- package/skills/rust-in-action/examples/after.md +156 -0
- package/skills/rust-in-action/examples/before.md +56 -0
- package/skills/rust-in-action/references/practices-catalog.md +346 -0
- package/skills/rust-in-action/scripts/review.py +147 -0
- package/skills/skill-router/SKILL.md +16 -13
- package/skills/skill-router/references/skill-catalog.md +19 -1
- package/skills/spring-boot-in-action/SKILL.md +312 -0
- package/skills/spring-boot-in-action/evals/evals.json +39 -0
- package/skills/spring-boot-in-action/examples/after.md +185 -0
- package/skills/spring-boot-in-action/examples/before.md +84 -0
- package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
- package/skills/spring-boot-in-action/scripts/review.py +184 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# After: Programming with Rust
|
|
2
|
+
|
|
3
|
+
The same service rewritten with idiomatic Rust — proper ownership, Result-based error handling, borrowing over cloning, iterator adapters, and trait bounds.
|
|
4
|
+
|
|
5
|
+
```rust
|
|
6
|
+
use std::collections::HashMap;
|
|
7
|
+
use std::fmt;
|
|
8
|
+
use std::sync::{Arc, Mutex};
|
|
9
|
+
|
|
10
|
+
// Custom error type — callers can match on variants (Ch 12)
|
|
11
|
+
#[derive(Debug)]
|
|
12
|
+
pub enum ConfigError {
|
|
13
|
+
Io(std::io::Error),
|
|
14
|
+
UserNotFound(String),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl fmt::Display for ConfigError {
|
|
18
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
19
|
+
match self {
|
|
20
|
+
ConfigError::Io(e) => write!(f, "I/O error: {e}"),
|
|
21
|
+
ConfigError::UserNotFound(name) => write!(f, "user not found: {name}"),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl std::error::Error for ConfigError {}
|
|
27
|
+
|
|
28
|
+
impl From<std::io::Error> for ConfigError {
|
|
29
|
+
fn from(e: std::io::Error) -> Self {
|
|
30
|
+
ConfigError::Io(e)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// &str instead of String — works with both literals and String via deref coercion (Ch 4)
|
|
35
|
+
// Returns Result — caller decides how to handle failure (Ch 12)
|
|
36
|
+
fn load_config(path: &str) -> Result<String, ConfigError> {
|
|
37
|
+
let content = std::fs::read_to_string(path)?; // ? propagates I/O error (Ch 12)
|
|
38
|
+
Ok(content)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Borrows the map — no clone, caller retains ownership (Ch 8, 10)
|
|
42
|
+
fn get_user<'a>(users: &'a HashMap<String, String>, name: &str) -> Result<&'a str, ConfigError> {
|
|
43
|
+
users
|
|
44
|
+
.get(name)
|
|
45
|
+
.map(|s| s.as_str())
|
|
46
|
+
.ok_or_else(|| ConfigError::UserNotFound(name.to_string()))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Iterator adapters replace manual index loop (Ch 6)
|
|
50
|
+
fn active_names(users: &[(String, bool)]) -> Vec<&str> {
|
|
51
|
+
users
|
|
52
|
+
.iter()
|
|
53
|
+
.filter(|(_, active)| *active)
|
|
54
|
+
.map(|(name, _)| name.as_str())
|
|
55
|
+
.collect()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Safe shared counter with Arc<Mutex<T>> (Ch 19)
|
|
59
|
+
fn make_counter() -> Arc<Mutex<u32>> {
|
|
60
|
+
Arc::new(Mutex::new(0))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fn increment(counter: &Arc<Mutex<u32>>) {
|
|
64
|
+
let mut count = counter.lock().expect("mutex poisoned");
|
|
65
|
+
*count += 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Trait bound instead of concrete type — accepts anything Display (Ch 14, 17)
|
|
69
|
+
fn print_item(item: &impl fmt::Display) {
|
|
70
|
+
println!("{item}");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn main() -> Result<(), ConfigError> {
|
|
74
|
+
let config = load_config("config.toml")?; // ? instead of unwrap (Ch 12)
|
|
75
|
+
println!("{config}");
|
|
76
|
+
|
|
77
|
+
let mut users = HashMap::new();
|
|
78
|
+
users.insert(String::from("alice"), String::from("admin"));
|
|
79
|
+
|
|
80
|
+
// Borrow users — still usable after the call (Ch 8)
|
|
81
|
+
let role = get_user(&users, "alice")?;
|
|
82
|
+
println!("{role}");
|
|
83
|
+
println!("{users:?}"); // still valid — was only borrowed
|
|
84
|
+
|
|
85
|
+
let items = vec![
|
|
86
|
+
(String::from("alice"), true),
|
|
87
|
+
(String::from("bob"), false),
|
|
88
|
+
];
|
|
89
|
+
let names = active_names(&items);
|
|
90
|
+
println!("{names:?}");
|
|
91
|
+
|
|
92
|
+
let counter = make_counter();
|
|
93
|
+
increment(&counter);
|
|
94
|
+
println!("count: {}", counter.lock().unwrap());
|
|
95
|
+
|
|
96
|
+
Ok(())
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Key improvements:**
|
|
101
|
+
- `ConfigError` gives callers structured, matchable errors (Ch 12)
|
|
102
|
+
- `?` replaces `.unwrap()` for clean error propagation (Ch 12)
|
|
103
|
+
- `&str` / `&HashMap` parameters borrow instead of consuming (Ch 4, 8, 10)
|
|
104
|
+
- Iterator adapters replace manual index loops (Ch 6)
|
|
105
|
+
- `Arc<Mutex<T>>` replaces `unsafe static mut` for safe shared state (Ch 19)
|
|
106
|
+
- `impl fmt::Display` trait bound instead of concrete `String` parameter (Ch 14, 17)
|
|
107
|
+
- `main()` returns `Result` so errors surface cleanly (Ch 12)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Before: Programming with Rust
|
|
2
|
+
|
|
3
|
+
A user service with common Rust anti-patterns — ownership misuse, panic-prone error handling, unnecessary cloning, and poor trait design.
|
|
4
|
+
|
|
5
|
+
```rust
|
|
6
|
+
use std::collections::HashMap;
|
|
7
|
+
|
|
8
|
+
// No custom error type — callers can't match on failures
|
|
9
|
+
fn load_config(path: String) -> String { // takes owned String unnecessarily
|
|
10
|
+
std::fs::read_to_string(path).unwrap() // panics on any I/O error
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Clones entire map just to read it
|
|
14
|
+
fn get_user(users: HashMap<String, String>, name: String) -> String {
|
|
15
|
+
let users_copy = users.clone(); // unnecessary clone of the whole map
|
|
16
|
+
match users_copy.get(&name) {
|
|
17
|
+
Some(user) => user.clone(),
|
|
18
|
+
None => String::from("unknown"), // silently returns fallback — hides missing users
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Accumulates with a manual loop instead of iterator adapters
|
|
23
|
+
fn active_names(users: Vec<(String, bool)>) -> Vec<String> {
|
|
24
|
+
let mut result = Vec::new();
|
|
25
|
+
for i in 0..users.len() {
|
|
26
|
+
if users[i].1 == true {
|
|
27
|
+
result.push(users[i].0.clone());
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
result
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Shared state with no synchronization
|
|
34
|
+
static mut COUNTER: u32 = 0;
|
|
35
|
+
|
|
36
|
+
fn increment() {
|
|
37
|
+
unsafe {
|
|
38
|
+
COUNTER += 1; // data race — undefined behavior in concurrent code
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Concrete type parameter instead of trait bound
|
|
43
|
+
fn print_user(user: String) {
|
|
44
|
+
println!("{}", user);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn main() {
|
|
48
|
+
let config = load_config(String::from("config.toml"));
|
|
49
|
+
println!("{}", config);
|
|
50
|
+
|
|
51
|
+
let mut users = HashMap::new();
|
|
52
|
+
users.insert(String::from("alice"), String::from("admin"));
|
|
53
|
+
|
|
54
|
+
// Moves users into get_user — can't use it after
|
|
55
|
+
let name = get_user(users, String::from("alice"));
|
|
56
|
+
println!("{}", name);
|
|
57
|
+
// println!("{:?}", users); // would fail: users was moved
|
|
58
|
+
}
|
|
59
|
+
```
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Programming with Rust — Chapter Reference
|
|
2
|
+
|
|
3
|
+
All 23 chapters from Donis Marshall's "Programming with Rust" with key topics and priority levels.
|
|
4
|
+
|
|
5
|
+
## Ch 1: Introduction to Rust
|
|
6
|
+
**Key topics:** Functional programming, expression-oriented, pattern-oriented, safeness, ownership, lifetimes, fearless concurrency, zero-cost abstraction, Rust terminology, tools
|
|
7
|
+
**Priority:** Foundation
|
|
8
|
+
|
|
9
|
+
## Ch 2: Getting Started
|
|
10
|
+
**Key topics:** Cargo, crates, library vs binary, `main` function, command-line arguments, comments, `rustup`
|
|
11
|
+
**Priority:** Foundation
|
|
12
|
+
|
|
13
|
+
## Ch 3: Variables
|
|
14
|
+
**Key topics:** Immutability by default (`let` vs `let mut`), integer types, overflow behavior, floating point, boolean, char, pointers, references, operators
|
|
15
|
+
**Priority:** Important — immutability by default is a key Rust safety idiom
|
|
16
|
+
|
|
17
|
+
## Ch 4: Strings
|
|
18
|
+
**Key topics:** `&str` (string slice, stack) vs `String` (heap-allocated, owned), deref coercion, `format!`, helpful string functions
|
|
19
|
+
**Idiom:** Accept `&str` in function parameters — `String` derefs to `&str` automatically
|
|
20
|
+
**Priority:** Important
|
|
21
|
+
|
|
22
|
+
## Ch 5: Console
|
|
23
|
+
**Key topics:** `println!`, positional/variable/named arguments, padding/alignment, `Display` trait, `Debug` trait, `format!`, `write!`
|
|
24
|
+
**Priority:** Suggestion
|
|
25
|
+
|
|
26
|
+
## Ch 6: Control Flow
|
|
27
|
+
**Key topics:** `if` as expression, `while`, `for`/`in` (iterator-based), `loop`, `break`/`continue`, loop labels, `Iterator` trait
|
|
28
|
+
**Idiom:** Use `for item in collection` over manual indexing; use iterator adapters over manual loops
|
|
29
|
+
**Priority:** Important
|
|
30
|
+
|
|
31
|
+
## Ch 7: Collections
|
|
32
|
+
**Key topics:** Arrays (fixed-size, stack), slices, Vecs (heap, growable), multidimensional, `HashMap` (entry API, iteration, update)
|
|
33
|
+
**Priority:** Important
|
|
34
|
+
|
|
35
|
+
## Ch 8: Ownership
|
|
36
|
+
**Key topics:** Stack vs heap, shallow vs deep copy, move semantics, borrow, copy semantics, `Clone` trait, `Copy` trait
|
|
37
|
+
**Critical rules:**
|
|
38
|
+
- Each value has exactly one owner
|
|
39
|
+
- When owner goes out of scope, value is dropped
|
|
40
|
+
- Move transfers ownership — old binding is invalid
|
|
41
|
+
- `Copy` types (primitives) are copied instead of moved
|
|
42
|
+
**Priority:** Critical
|
|
43
|
+
|
|
44
|
+
## Ch 9: Lifetimes
|
|
45
|
+
**Key topics:** Lifetime annotation syntax (`'a`), lifetime elision rules, function headers, complex lifetimes, `'static`, structs/methods with lifetimes, subtyping, anonymous lifetimes, generics and lifetimes
|
|
46
|
+
**Idiom:** Rely on elision; annotate only when compiler cannot infer (multiple reference params with ambiguous output lifetime)
|
|
47
|
+
**Priority:** Critical
|
|
48
|
+
|
|
49
|
+
## Ch 10: References
|
|
50
|
+
**Key topics:** Declaration, borrowing, dereferencing, comparing references, reference notation, mutability, limits to multiple borrowers
|
|
51
|
+
**Critical rules:**
|
|
52
|
+
- At any time: one `&mut T` OR any number of `&T` — never both
|
|
53
|
+
- References must not outlive the referent
|
|
54
|
+
**Priority:** Critical
|
|
55
|
+
|
|
56
|
+
## Ch 11: Functions
|
|
57
|
+
**Key topics:** Function definition, parameters, return values, `const fn`, nested functions, function pointers, function aliases
|
|
58
|
+
**Priority:** Important
|
|
59
|
+
|
|
60
|
+
## Ch 12: Error Handling
|
|
61
|
+
**Key topics:** `Result<T, E>`, `Option<T>`, panics, `panic!` macro, handling panics, `.unwrap()`, `.expect()`, `?` operator, `match` on Result/Option, `.map()`, rich errors, custom error types
|
|
62
|
+
**Critical rules:**
|
|
63
|
+
- Library code: always `Result`, never panic
|
|
64
|
+
- Use `?` for propagation, not `.unwrap()`
|
|
65
|
+
- Define custom error types implementing `std::error::Error`
|
|
66
|
+
- Implement `From<E>` for automatic `?` conversion
|
|
67
|
+
**Priority:** Critical
|
|
68
|
+
|
|
69
|
+
## Ch 13: Structures
|
|
70
|
+
**Key topics:** Struct definition, alternate initialization, move semantics with structs, mutability, methods (`&self`, `&mut self`, `self`), associated functions (`new`), `impl` blocks, operator overloading, tuple structs, unit-like structs
|
|
71
|
+
**Priority:** Important
|
|
72
|
+
|
|
73
|
+
## Ch 14: Generics
|
|
74
|
+
**Key topics:** Generic functions, trait bounds, `where` clause, generic structs, associated functions, generic enums, generic traits, explicit specialization
|
|
75
|
+
**Idiom:** Use `where` clause for complex bounds; prefer narrowest possible bounds
|
|
76
|
+
**Priority:** Important
|
|
77
|
+
|
|
78
|
+
## Ch 15: Patterns
|
|
79
|
+
**Key topics:** `let` destructuring, wildcards (`_`), complex patterns, ownership in patterns, irrefutable patterns, ranges, multiple patterns (`|`), control flow patterns, struct destructuring, function parameter patterns, `match` expressions, match guards
|
|
80
|
+
**Idiom:** `match` is exhaustive — let the compiler enforce all cases; use `if let` for single-variant matches
|
|
81
|
+
**Priority:** Important
|
|
82
|
+
|
|
83
|
+
## Ch 16: Closures
|
|
84
|
+
**Key topics:** Closure syntax, captured variables, closures as arguments/return values, `Fn` (immutable borrow), `FnMut` (mutable borrow), `FnOnce` (move/consume), `move` keyword, `impl` keyword for return types
|
|
85
|
+
**Idiom:** Use `move` closures when passing to threads to transfer ownership of captured values
|
|
86
|
+
**Priority:** Important
|
|
87
|
+
|
|
88
|
+
## Ch 17: Traits
|
|
89
|
+
**Key topics:** Trait definition, default functions, marker traits (`Send`, `Sync`), associated functions, associated types, extension methods, fully qualified syntax, supertraits, static dispatch (`impl Trait`), dynamic dispatch (`dyn Trait`), enums and traits
|
|
90
|
+
**Critical rules:**
|
|
91
|
+
- Prefer `impl Trait` (static, zero-cost) over `dyn Trait` (runtime overhead via vtable)
|
|
92
|
+
- Use `dyn Trait` only when returning heterogeneous types or building plugin systems
|
|
93
|
+
**Priority:** Critical
|
|
94
|
+
|
|
95
|
+
## Ch 18: Threads 1
|
|
96
|
+
**Key topics:** Synchronous vs asynchronous calls, `std::thread::spawn`, thread type, `Builder`, CSP (Communicating Sequential Process), async/sync/rendezvous channels (`mpsc`), `try_recv`, store example
|
|
97
|
+
**Idiom:** Prefer message passing (channels) over shared state when possible
|
|
98
|
+
**Priority:** Important
|
|
99
|
+
|
|
100
|
+
## Ch 19: Threads 2
|
|
101
|
+
**Key topics:** `Mutex<T>`, nonscoped mutex, mutex poisoning, `RwLock<T>`, condition variables (`Condvar`), atomic operations (`AtomicUsize`, etc.), store/load, fetch-and-modify, compare-and-exchange
|
|
102
|
+
**Idiom:** Use `Arc<Mutex<T>>` for shared mutable state; `Arc<RwLock<T>>` for read-heavy workloads
|
|
103
|
+
**Priority:** Critical
|
|
104
|
+
|
|
105
|
+
## Ch 20: Memory
|
|
106
|
+
**Key topics:** Stack vs heap allocation, static values, `Box<T>` (heap allocation), interior mutability pattern, `RefCell<T>` (runtime borrow checking, single-threaded), `OnceCell<T>` (lazy initialization)
|
|
107
|
+
**Idiom:** Stack-allocate by default; use `Box` only when size is unknown at compile time or for recursive types
|
|
108
|
+
**Priority:** Important
|
|
109
|
+
|
|
110
|
+
## Ch 21: Macros
|
|
111
|
+
**Key topics:** Tokens, declarative macros (`macro_rules!`), repetition, multiple matchers, procedural macros (derive, attribute, function-like), `#[derive(...)]`
|
|
112
|
+
**Idiom:** Use `#[derive]` before writing manual `impl` blocks; write `macro_rules!` for repeated patterns
|
|
113
|
+
**Priority:** Suggestion
|
|
114
|
+
|
|
115
|
+
## Ch 22: Interoperability
|
|
116
|
+
**Key topics:** FFI, `extern "C"`, `unsafe` blocks, `libc` crate, structs across FFI, `bindgen` (C → Rust), `cbindgen` (Rust → C)
|
|
117
|
+
**Priority:** Suggestion (only when needed)
|
|
118
|
+
|
|
119
|
+
## Ch 23: Modules
|
|
120
|
+
**Key topics:** Module items, `pub` visibility, module files, `path` attribute, functions and modules, `crate`/`super`/`self` keywords, legacy module model
|
|
121
|
+
**Idiom:** Organize by domain, not by type; keep `pub` surface minimal; use `pub(crate)` for internal-only APIs
|
|
122
|
+
**Priority:** Important
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Priority Summary
|
|
127
|
+
|
|
128
|
+
**Critical (understand deeply before writing any Rust)**
|
|
129
|
+
- Ch 8: Ownership and move semantics
|
|
130
|
+
- Ch 9: Lifetimes and elision
|
|
131
|
+
- Ch 10: Borrowing rules (one `&mut` OR many `&`)
|
|
132
|
+
- Ch 12: Error handling with Result, ?, custom errors
|
|
133
|
+
- Ch 17: Trait dispatch (impl vs dyn)
|
|
134
|
+
- Ch 19: Thread safety with Mutex/RwLock/Arc
|
|
135
|
+
|
|
136
|
+
**Important (apply consistently)**
|
|
137
|
+
- Ch 3: Immutability by default
|
|
138
|
+
- Ch 4: `&str` vs `String`
|
|
139
|
+
- Ch 6: Iterator-based loops
|
|
140
|
+
- Ch 11: Function design
|
|
141
|
+
- Ch 13: Structs and impl blocks
|
|
142
|
+
- Ch 14: Generics and bounds
|
|
143
|
+
- Ch 15: Exhaustive pattern matching
|
|
144
|
+
- Ch 16: Closures and move semantics
|
|
145
|
+
- Ch 18: Channels for concurrency
|
|
146
|
+
- Ch 20: Stack vs heap, RefCell
|
|
147
|
+
- Ch 23: Module visibility
|
|
148
|
+
|
|
149
|
+
**Suggestion (apply when relevant)**
|
|
150
|
+
- Ch 5: Formatting and Display/Debug
|
|
151
|
+
- Ch 21: Macros and derive
|
|
152
|
+
- Ch 22: FFI/interop
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Programming with Rust — Practices Catalog
|
|
2
|
+
|
|
3
|
+
Deep before/after examples for the most critical Rust idioms from each chapter group.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Ownership: Borrow Instead of Clone (Ch 8)
|
|
8
|
+
|
|
9
|
+
**Before:**
|
|
10
|
+
```rust
|
|
11
|
+
fn print_names(names: Vec<String>) { // consumes the Vec
|
|
12
|
+
for name in names {
|
|
13
|
+
println!("{name}");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// names is gone after this call
|
|
17
|
+
```
|
|
18
|
+
**After:**
|
|
19
|
+
```rust
|
|
20
|
+
fn print_names(names: &[String]) { // borrows a slice — Vec<String> derefs to &[String]
|
|
21
|
+
for name in names {
|
|
22
|
+
println!("{name}");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// caller keeps ownership
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Ownership: Move Semantics (Ch 8)
|
|
31
|
+
|
|
32
|
+
**Before:**
|
|
33
|
+
```rust
|
|
34
|
+
let s1 = String::from("hello");
|
|
35
|
+
let s2 = s1; // s1 is MOVED into s2
|
|
36
|
+
println!("{s1}"); // compile error: s1 was moved
|
|
37
|
+
```
|
|
38
|
+
**After:**
|
|
39
|
+
```rust
|
|
40
|
+
let s1 = String::from("hello");
|
|
41
|
+
let s2 = s1.clone(); // explicit deep copy — both valid
|
|
42
|
+
println!("{s1} {s2}"); // both valid
|
|
43
|
+
|
|
44
|
+
// Or: borrow instead of cloning
|
|
45
|
+
let s3 = &s1; // borrow — s1 still owns the data
|
|
46
|
+
println!("{s1} {s3}");
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## References: Borrow Rules (Ch 10)
|
|
52
|
+
|
|
53
|
+
```rust
|
|
54
|
+
let mut data = vec![1, 2, 3];
|
|
55
|
+
|
|
56
|
+
// OK: multiple immutable borrows
|
|
57
|
+
let a = &data;
|
|
58
|
+
let b = &data;
|
|
59
|
+
println!("{a:?} {b:?}");
|
|
60
|
+
|
|
61
|
+
// OK: one mutable borrow (after immutable borrows end)
|
|
62
|
+
let c = &mut data;
|
|
63
|
+
c.push(4);
|
|
64
|
+
|
|
65
|
+
// COMPILE ERROR: can't have mutable + immutable borrow at same time
|
|
66
|
+
// let d = &data;
|
|
67
|
+
// let e = &mut data; // error
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Lifetimes: Elision vs Annotation (Ch 9)
|
|
73
|
+
|
|
74
|
+
**Elision works (no annotation needed):**
|
|
75
|
+
```rust
|
|
76
|
+
fn first_word(s: &str) -> &str { // compiler infers output lifetime = input lifetime
|
|
77
|
+
s.split_whitespace().next().unwrap_or("")
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Annotation required (multiple inputs, ambiguous output):**
|
|
82
|
+
```rust
|
|
83
|
+
// Without annotation — compiler can't tell which input the output borrows from
|
|
84
|
+
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
|
|
85
|
+
if s1.len() > s2.len() { s1 } else { s2 }
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Struct holding a reference — must annotate:**
|
|
90
|
+
```rust
|
|
91
|
+
struct Excerpt<'a> {
|
|
92
|
+
text: &'a str, // struct can't outlive the string it borrows
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Error Handling: Result + ? (Ch 12)
|
|
99
|
+
|
|
100
|
+
**Before:**
|
|
101
|
+
```rust
|
|
102
|
+
fn read_username() -> String {
|
|
103
|
+
let f = std::fs::File::open("username.txt").unwrap();
|
|
104
|
+
let mut s = String::new();
|
|
105
|
+
std::io::Read::read_to_string(&mut std::io::BufReader::new(f), &mut s).unwrap();
|
|
106
|
+
s
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
**After:**
|
|
110
|
+
```rust
|
|
111
|
+
fn read_username() -> Result<String, std::io::Error> {
|
|
112
|
+
std::fs::read_to_string("username.txt") // ? propagates automatically
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Error Handling: Custom Error Types (Ch 12)
|
|
119
|
+
|
|
120
|
+
```rust
|
|
121
|
+
use std::fmt;
|
|
122
|
+
|
|
123
|
+
#[derive(Debug)]
|
|
124
|
+
pub enum AppError {
|
|
125
|
+
Io(std::io::Error),
|
|
126
|
+
Parse(std::num::ParseIntError),
|
|
127
|
+
Custom(String),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
impl fmt::Display for AppError {
|
|
131
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
132
|
+
match self {
|
|
133
|
+
AppError::Io(e) => write!(f, "I/O: {e}"),
|
|
134
|
+
AppError::Parse(e) => write!(f, "parse: {e}"),
|
|
135
|
+
AppError::Custom(s) => write!(f, "{s}"),
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
impl std::error::Error for AppError {}
|
|
141
|
+
|
|
142
|
+
// Enables ? to convert std::io::Error → AppError automatically
|
|
143
|
+
impl From<std::io::Error> for AppError {
|
|
144
|
+
fn from(e: std::io::Error) -> Self { AppError::Io(e) }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
impl From<std::num::ParseIntError> for AppError {
|
|
148
|
+
fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn parse_port(s: &str) -> Result<u16, AppError> {
|
|
152
|
+
let port: i32 = s.parse()?; // ParseIntError → AppError via From
|
|
153
|
+
if port < 1 || port > 65535 {
|
|
154
|
+
return Err(AppError::Custom(format!("invalid port: {port}")));
|
|
155
|
+
}
|
|
156
|
+
Ok(port as u16)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Traits: Static vs Dynamic Dispatch (Ch 17)
|
|
163
|
+
|
|
164
|
+
**Static dispatch — zero cost, preferred:**
|
|
165
|
+
```rust
|
|
166
|
+
// Monomorphized at compile time — no runtime overhead
|
|
167
|
+
fn notify(item: &impl Summary) {
|
|
168
|
+
println!("Breaking news! {}", item.summarize());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Equivalent with explicit generic:
|
|
172
|
+
fn notify<T: Summary>(item: &T) {
|
|
173
|
+
println!("Breaking news! {}", item.summarize());
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Dynamic dispatch — use only for heterogeneous collections or plugins:**
|
|
178
|
+
```rust
|
|
179
|
+
// Vtable lookup at runtime — small overhead, but enables mixed types
|
|
180
|
+
fn notify_all(items: &[Box<dyn Summary>]) {
|
|
181
|
+
for item in items {
|
|
182
|
+
println!("{}", item.summarize());
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Traits: Implementing Standard Traits (Ch 17)
|
|
190
|
+
|
|
191
|
+
```rust
|
|
192
|
+
use std::fmt;
|
|
193
|
+
|
|
194
|
+
#[derive(Debug, Clone, PartialEq)] // derive common traits
|
|
195
|
+
struct Point {
|
|
196
|
+
x: f64,
|
|
197
|
+
y: f64,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Display for user-facing output
|
|
201
|
+
impl fmt::Display for Point {
|
|
202
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
203
|
+
write!(f, "({}, {})", self.x, self.y)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// From for ergonomic conversion
|
|
208
|
+
impl From<(f64, f64)> for Point {
|
|
209
|
+
fn from((x, y): (f64, f64)) -> Self {
|
|
210
|
+
Point { x, y }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let p: Point = (1.0, 2.0).into(); // uses From impl
|
|
215
|
+
println!("{p}"); // uses Display
|
|
216
|
+
println!("{p:?}"); // uses Debug
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Pattern Matching: Exhaustive match (Ch 15)
|
|
222
|
+
|
|
223
|
+
**Before:**
|
|
224
|
+
```rust
|
|
225
|
+
let opt: Option<i32> = get_value();
|
|
226
|
+
match opt {
|
|
227
|
+
Some(x) => println!("{x}"),
|
|
228
|
+
_ => {} // silently ignores None — intent unclear
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
**After:**
|
|
232
|
+
```rust
|
|
233
|
+
// if let for single-variant match
|
|
234
|
+
if let Some(x) = get_value() {
|
|
235
|
+
println!("{x}");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Exhaustive match when both branches matter
|
|
239
|
+
match get_value() {
|
|
240
|
+
Some(x) => println!("got {x}"),
|
|
241
|
+
None => println!("nothing"),
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Closures: move for Threads (Ch 16, 18)
|
|
248
|
+
|
|
249
|
+
**Before:**
|
|
250
|
+
```rust
|
|
251
|
+
let name = String::from("Alice");
|
|
252
|
+
let handle = std::thread::spawn(|| {
|
|
253
|
+
println!("{name}"); // error: closure may outlive name
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
**After:**
|
|
257
|
+
```rust
|
|
258
|
+
let name = String::from("Alice");
|
|
259
|
+
let handle = std::thread::spawn(move || {
|
|
260
|
+
println!("{name}"); // name is moved into the closure — safe
|
|
261
|
+
});
|
|
262
|
+
handle.join().unwrap();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Concurrency: Arc<Mutex<T>> (Ch 19)
|
|
268
|
+
|
|
269
|
+
```rust
|
|
270
|
+
use std::sync::{Arc, Mutex};
|
|
271
|
+
use std::thread;
|
|
272
|
+
|
|
273
|
+
fn main() {
|
|
274
|
+
let counter = Arc::new(Mutex::new(0u32));
|
|
275
|
+
let mut handles = vec![];
|
|
276
|
+
|
|
277
|
+
for _ in 0..10 {
|
|
278
|
+
let counter = Arc::clone(&counter); // clone the Arc, not the data
|
|
279
|
+
let handle = thread::spawn(move || {
|
|
280
|
+
let mut num = counter.lock().expect("mutex poisoned");
|
|
281
|
+
*num += 1;
|
|
282
|
+
});
|
|
283
|
+
handles.push(handle);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for handle in handles {
|
|
287
|
+
handle.join().unwrap();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
println!("Result: {}", *counter.lock().unwrap()); // 10
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Memory: RefCell for Interior Mutability (Ch 20)
|
|
297
|
+
|
|
298
|
+
Use `RefCell<T>` when you need mutability inside an otherwise immutable value (single-threaded only):
|
|
299
|
+
|
|
300
|
+
```rust
|
|
301
|
+
use std::cell::RefCell;
|
|
302
|
+
|
|
303
|
+
struct Cache {
|
|
304
|
+
data: RefCell<Option<String>>, // interior mutability
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
impl Cache {
|
|
308
|
+
fn get(&self) -> String {
|
|
309
|
+
// borrow_mut even though &self is immutable
|
|
310
|
+
let mut data = self.data.borrow_mut();
|
|
311
|
+
if data.is_none() {
|
|
312
|
+
*data = Some(expensive_computation());
|
|
313
|
+
}
|
|
314
|
+
data.as_ref().unwrap().clone()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// For multi-threaded use, replace RefCell with Mutex
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Modules: Visibility (Ch 23)
|
|
323
|
+
|
|
324
|
+
```rust
|
|
325
|
+
// lib.rs
|
|
326
|
+
pub mod api { // public module
|
|
327
|
+
pub struct Request { /* ... */ } // public type
|
|
328
|
+
pub(crate) fn validate() { } // crate-internal only
|
|
329
|
+
fn internal_helper() { } // private to module
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
mod storage { // private module — not part of public API
|
|
333
|
+
pub(super) fn save() { } // accessible to parent module only
|
|
334
|
+
}
|
|
335
|
+
```
|