@booklib/skills 1.3.1 → 1.4.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.
Files changed (202) hide show
  1. package/AGENTS.md +108 -0
  2. package/CLAUDE.md +57 -0
  3. package/CODE_OF_CONDUCT.md +31 -0
  4. package/CONTRIBUTING.md +15 -2
  5. package/README.md +70 -45
  6. package/SECURITY.md +9 -0
  7. package/assets/logo.svg +36 -0
  8. package/bin/skills.js +1 -1
  9. package/demo.gif +0 -0
  10. package/demo.tape +40 -0
  11. package/docs/index.html +187 -0
  12. package/package.json +11 -4
  13. package/skills/effective-typescript/SKILL.md +166 -0
  14. package/skills/effective-typescript/evals/evals.json +36 -0
  15. package/skills/effective-typescript/examples/after.md +70 -0
  16. package/skills/effective-typescript/examples/before.md +47 -0
  17. package/skills/effective-typescript/references/api_reference.md +118 -0
  18. package/skills/effective-typescript/references/practices-catalog.md +371 -0
  19. package/skills/programming-with-rust/SKILL.md +194 -0
  20. package/skills/programming-with-rust/evals/evals.json +37 -0
  21. package/skills/programming-with-rust/examples/after.md +107 -0
  22. package/skills/programming-with-rust/examples/before.md +59 -0
  23. package/skills/programming-with-rust/references/api_reference.md +152 -0
  24. package/skills/programming-with-rust/references/practices-catalog.md +335 -0
  25. package/skills/rust-in-action/SKILL.md +290 -0
  26. package/skills/rust-in-action/evals/evals.json +38 -0
  27. package/skills/rust-in-action/examples/after.md +156 -0
  28. package/skills/rust-in-action/examples/before.md +56 -0
  29. package/skills/rust-in-action/references/practices-catalog.md +346 -0
  30. package/skills/rust-in-action/scripts/review.py +147 -0
  31. package/{skill-router → skills/skill-router}/SKILL.md +16 -13
  32. package/{skill-router → skills/skill-router}/references/skill-catalog.md +19 -1
  33. /package/{animation-at-work → skills/animation-at-work}/SKILL.md +0 -0
  34. /package/{animation-at-work → skills/animation-at-work}/assets/example_asset.txt +0 -0
  35. /package/{animation-at-work → skills/animation-at-work}/evals/evals.json +0 -0
  36. /package/{animation-at-work → skills/animation-at-work}/examples/after.md +0 -0
  37. /package/{animation-at-work → skills/animation-at-work}/examples/before.md +0 -0
  38. /package/{animation-at-work → skills/animation-at-work}/references/api_reference.md +0 -0
  39. /package/{animation-at-work → skills/animation-at-work}/references/review-checklist.md +0 -0
  40. /package/{animation-at-work → skills/animation-at-work}/scripts/audit_animations.py +0 -0
  41. /package/{animation-at-work → skills/animation-at-work}/scripts/example.py +0 -0
  42. /package/{clean-code-reviewer → skills/clean-code-reviewer}/SKILL.md +0 -0
  43. /package/{clean-code-reviewer → skills/clean-code-reviewer}/evals/evals.json +0 -0
  44. /package/{clean-code-reviewer → skills/clean-code-reviewer}/examples/after.md +0 -0
  45. /package/{clean-code-reviewer → skills/clean-code-reviewer}/examples/before.md +0 -0
  46. /package/{clean-code-reviewer → skills/clean-code-reviewer}/references/api_reference.md +0 -0
  47. /package/{clean-code-reviewer → skills/clean-code-reviewer}/references/practices-catalog.md +0 -0
  48. /package/{clean-code-reviewer → skills/clean-code-reviewer}/references/review-checklist.md +0 -0
  49. /package/{clean-code-reviewer → skills/clean-code-reviewer}/scripts/pre-review.py +0 -0
  50. /package/{data-intensive-patterns → skills/data-intensive-patterns}/SKILL.md +0 -0
  51. /package/{data-intensive-patterns → skills/data-intensive-patterns}/assets/example_asset.txt +0 -0
  52. /package/{data-intensive-patterns → skills/data-intensive-patterns}/evals/evals.json +0 -0
  53. /package/{data-intensive-patterns → skills/data-intensive-patterns}/examples/after.md +0 -0
  54. /package/{data-intensive-patterns → skills/data-intensive-patterns}/examples/before.md +0 -0
  55. /package/{data-intensive-patterns → skills/data-intensive-patterns}/references/api_reference.md +0 -0
  56. /package/{data-intensive-patterns → skills/data-intensive-patterns}/references/patterns-catalog.md +0 -0
  57. /package/{data-intensive-patterns → skills/data-intensive-patterns}/references/review-checklist.md +0 -0
  58. /package/{data-intensive-patterns → skills/data-intensive-patterns}/scripts/adr.py +0 -0
  59. /package/{data-intensive-patterns → skills/data-intensive-patterns}/scripts/example.py +0 -0
  60. /package/{data-pipelines → skills/data-pipelines}/SKILL.md +0 -0
  61. /package/{data-pipelines → skills/data-pipelines}/assets/example_asset.txt +0 -0
  62. /package/{data-pipelines → skills/data-pipelines}/evals/evals.json +0 -0
  63. /package/{data-pipelines → skills/data-pipelines}/examples/after.md +0 -0
  64. /package/{data-pipelines → skills/data-pipelines}/examples/before.md +0 -0
  65. /package/{data-pipelines → skills/data-pipelines}/references/api_reference.md +0 -0
  66. /package/{data-pipelines → skills/data-pipelines}/references/review-checklist.md +0 -0
  67. /package/{data-pipelines → skills/data-pipelines}/scripts/example.py +0 -0
  68. /package/{data-pipelines → skills/data-pipelines}/scripts/new_pipeline.py +0 -0
  69. /package/{design-patterns → skills/design-patterns}/SKILL.md +0 -0
  70. /package/{design-patterns → skills/design-patterns}/assets/example_asset.txt +0 -0
  71. /package/{design-patterns → skills/design-patterns}/evals/evals.json +0 -0
  72. /package/{design-patterns → skills/design-patterns}/examples/after.md +0 -0
  73. /package/{design-patterns → skills/design-patterns}/examples/before.md +0 -0
  74. /package/{design-patterns → skills/design-patterns}/references/api_reference.md +0 -0
  75. /package/{design-patterns → skills/design-patterns}/references/patterns-catalog.md +0 -0
  76. /package/{design-patterns → skills/design-patterns}/references/review-checklist.md +0 -0
  77. /package/{design-patterns → skills/design-patterns}/scripts/example.py +0 -0
  78. /package/{design-patterns → skills/design-patterns}/scripts/scaffold.py +0 -0
  79. /package/{domain-driven-design → skills/domain-driven-design}/SKILL.md +0 -0
  80. /package/{domain-driven-design → skills/domain-driven-design}/assets/example_asset.txt +0 -0
  81. /package/{domain-driven-design → skills/domain-driven-design}/evals/evals.json +0 -0
  82. /package/{domain-driven-design → skills/domain-driven-design}/examples/after.md +0 -0
  83. /package/{domain-driven-design → skills/domain-driven-design}/examples/before.md +0 -0
  84. /package/{domain-driven-design → skills/domain-driven-design}/references/api_reference.md +0 -0
  85. /package/{domain-driven-design → skills/domain-driven-design}/references/patterns-catalog.md +0 -0
  86. /package/{domain-driven-design → skills/domain-driven-design}/references/review-checklist.md +0 -0
  87. /package/{domain-driven-design → skills/domain-driven-design}/scripts/example.py +0 -0
  88. /package/{domain-driven-design → skills/domain-driven-design}/scripts/scaffold.py +0 -0
  89. /package/{effective-java → skills/effective-java}/SKILL.md +0 -0
  90. /package/{effective-java → skills/effective-java}/assets/example_asset.txt +0 -0
  91. /package/{effective-java → skills/effective-java}/evals/evals.json +0 -0
  92. /package/{effective-java → skills/effective-java}/examples/after.md +0 -0
  93. /package/{effective-java → skills/effective-java}/examples/before.md +0 -0
  94. /package/{effective-java → skills/effective-java}/references/api_reference.md +0 -0
  95. /package/{effective-java → skills/effective-java}/references/items-catalog.md +0 -0
  96. /package/{effective-java → skills/effective-java}/references/review-checklist.md +0 -0
  97. /package/{effective-java → skills/effective-java}/scripts/checkstyle_setup.py +0 -0
  98. /package/{effective-java → skills/effective-java}/scripts/example.py +0 -0
  99. /package/{effective-kotlin → skills/effective-kotlin}/SKILL.md +0 -0
  100. /package/{effective-kotlin → skills/effective-kotlin}/assets/example_asset.txt +0 -0
  101. /package/{effective-kotlin → skills/effective-kotlin}/evals/evals.json +0 -0
  102. /package/{effective-kotlin → skills/effective-kotlin}/examples/after.md +0 -0
  103. /package/{effective-kotlin → skills/effective-kotlin}/examples/before.md +0 -0
  104. /package/{effective-kotlin → skills/effective-kotlin}/references/api_reference.md +0 -0
  105. /package/{effective-kotlin → skills/effective-kotlin}/references/practices-catalog.md +0 -0
  106. /package/{effective-kotlin → skills/effective-kotlin}/references/review-checklist.md +0 -0
  107. /package/{effective-kotlin → skills/effective-kotlin}/scripts/example.py +0 -0
  108. /package/{effective-python → skills/effective-python}/SKILL.md +0 -0
  109. /package/{effective-python → skills/effective-python}/evals/evals.json +0 -0
  110. /package/{effective-python → skills/effective-python}/examples/after.md +0 -0
  111. /package/{effective-python → skills/effective-python}/examples/before.md +0 -0
  112. /package/{effective-python → skills/effective-python}/ref-01-pythonic-thinking.md +0 -0
  113. /package/{effective-python → skills/effective-python}/ref-02-lists-and-dicts.md +0 -0
  114. /package/{effective-python → skills/effective-python}/ref-03-functions.md +0 -0
  115. /package/{effective-python → skills/effective-python}/ref-04-comprehensions-generators.md +0 -0
  116. /package/{effective-python → skills/effective-python}/ref-05-classes-interfaces.md +0 -0
  117. /package/{effective-python → skills/effective-python}/ref-06-metaclasses-attributes.md +0 -0
  118. /package/{effective-python → skills/effective-python}/ref-07-concurrency.md +0 -0
  119. /package/{effective-python → skills/effective-python}/ref-08-robustness-performance.md +0 -0
  120. /package/{effective-python → skills/effective-python}/ref-09-testing-debugging.md +0 -0
  121. /package/{effective-python → skills/effective-python}/ref-10-collaboration.md +0 -0
  122. /package/{effective-python → skills/effective-python}/references/api_reference.md +0 -0
  123. /package/{effective-python → skills/effective-python}/references/practices-catalog.md +0 -0
  124. /package/{effective-python → skills/effective-python}/references/review-checklist.md +0 -0
  125. /package/{effective-python → skills/effective-python}/scripts/lint.py +0 -0
  126. /package/{kotlin-in-action → skills/kotlin-in-action}/SKILL.md +0 -0
  127. /package/{kotlin-in-action → skills/kotlin-in-action}/assets/example_asset.txt +0 -0
  128. /package/{kotlin-in-action → skills/kotlin-in-action}/evals/evals.json +0 -0
  129. /package/{kotlin-in-action → skills/kotlin-in-action}/examples/after.md +0 -0
  130. /package/{kotlin-in-action → skills/kotlin-in-action}/examples/before.md +0 -0
  131. /package/{kotlin-in-action → skills/kotlin-in-action}/references/api_reference.md +0 -0
  132. /package/{kotlin-in-action → skills/kotlin-in-action}/references/practices-catalog.md +0 -0
  133. /package/{kotlin-in-action → skills/kotlin-in-action}/references/review-checklist.md +0 -0
  134. /package/{kotlin-in-action → skills/kotlin-in-action}/scripts/example.py +0 -0
  135. /package/{kotlin-in-action → skills/kotlin-in-action}/scripts/setup_detekt.py +0 -0
  136. /package/{lean-startup → skills/lean-startup}/SKILL.md +0 -0
  137. /package/{lean-startup → skills/lean-startup}/assets/example_asset.txt +0 -0
  138. /package/{lean-startup → skills/lean-startup}/evals/evals.json +0 -0
  139. /package/{lean-startup → skills/lean-startup}/examples/after.md +0 -0
  140. /package/{lean-startup → skills/lean-startup}/examples/before.md +0 -0
  141. /package/{lean-startup → skills/lean-startup}/references/api_reference.md +0 -0
  142. /package/{lean-startup → skills/lean-startup}/references/review-checklist.md +0 -0
  143. /package/{lean-startup → skills/lean-startup}/scripts/example.py +0 -0
  144. /package/{lean-startup → skills/lean-startup}/scripts/new_experiment.py +0 -0
  145. /package/{microservices-patterns → skills/microservices-patterns}/SKILL.md +0 -0
  146. /package/{microservices-patterns → skills/microservices-patterns}/evals/evals.json +0 -0
  147. /package/{microservices-patterns → skills/microservices-patterns}/examples/after.md +0 -0
  148. /package/{microservices-patterns → skills/microservices-patterns}/examples/before.md +0 -0
  149. /package/{microservices-patterns → skills/microservices-patterns}/references/patterns-catalog.md +0 -0
  150. /package/{microservices-patterns → skills/microservices-patterns}/references/review-checklist.md +0 -0
  151. /package/{microservices-patterns → skills/microservices-patterns}/scripts/new_service.py +0 -0
  152. /package/{refactoring-ui → skills/refactoring-ui}/SKILL.md +0 -0
  153. /package/{refactoring-ui → skills/refactoring-ui}/assets/example_asset.txt +0 -0
  154. /package/{refactoring-ui → skills/refactoring-ui}/evals/evals.json +0 -0
  155. /package/{refactoring-ui → skills/refactoring-ui}/examples/after.md +0 -0
  156. /package/{refactoring-ui → skills/refactoring-ui}/examples/before.md +0 -0
  157. /package/{refactoring-ui → skills/refactoring-ui}/references/api_reference.md +0 -0
  158. /package/{refactoring-ui → skills/refactoring-ui}/references/review-checklist.md +0 -0
  159. /package/{refactoring-ui → skills/refactoring-ui}/scripts/audit_css.py +0 -0
  160. /package/{refactoring-ui → skills/refactoring-ui}/scripts/example.py +0 -0
  161. /package/{skill-router → skills/skill-router}/evals/evals.json +0 -0
  162. /package/{skill-router → skills/skill-router}/examples/after.md +0 -0
  163. /package/{skill-router → skills/skill-router}/examples/before.md +0 -0
  164. /package/{skill-router → skills/skill-router}/references/api_reference.md +0 -0
  165. /package/{skill-router → skills/skill-router}/references/routing-heuristics.md +0 -0
  166. /package/{skill-router → skills/skill-router}/scripts/route.py +0 -0
  167. /package/{storytelling-with-data → skills/storytelling-with-data}/SKILL.md +0 -0
  168. /package/{storytelling-with-data → skills/storytelling-with-data}/assets/example_asset.txt +0 -0
  169. /package/{storytelling-with-data → skills/storytelling-with-data}/evals/evals.json +0 -0
  170. /package/{storytelling-with-data → skills/storytelling-with-data}/examples/after.md +0 -0
  171. /package/{storytelling-with-data → skills/storytelling-with-data}/examples/before.md +0 -0
  172. /package/{storytelling-with-data → skills/storytelling-with-data}/references/api_reference.md +0 -0
  173. /package/{storytelling-with-data → skills/storytelling-with-data}/references/review-checklist.md +0 -0
  174. /package/{storytelling-with-data → skills/storytelling-with-data}/scripts/chart_review.py +0 -0
  175. /package/{storytelling-with-data → skills/storytelling-with-data}/scripts/example.py +0 -0
  176. /package/{system-design-interview → skills/system-design-interview}/SKILL.md +0 -0
  177. /package/{system-design-interview → skills/system-design-interview}/assets/example_asset.txt +0 -0
  178. /package/{system-design-interview → skills/system-design-interview}/evals/evals.json +0 -0
  179. /package/{system-design-interview → skills/system-design-interview}/examples/after.md +0 -0
  180. /package/{system-design-interview → skills/system-design-interview}/examples/before.md +0 -0
  181. /package/{system-design-interview → skills/system-design-interview}/references/api_reference.md +0 -0
  182. /package/{system-design-interview → skills/system-design-interview}/references/review-checklist.md +0 -0
  183. /package/{system-design-interview → skills/system-design-interview}/scripts/example.py +0 -0
  184. /package/{system-design-interview → skills/system-design-interview}/scripts/new_design.py +0 -0
  185. /package/{using-asyncio-python → skills/using-asyncio-python}/SKILL.md +0 -0
  186. /package/{using-asyncio-python → skills/using-asyncio-python}/assets/example_asset.txt +0 -0
  187. /package/{using-asyncio-python → skills/using-asyncio-python}/evals/evals.json +0 -0
  188. /package/{using-asyncio-python → skills/using-asyncio-python}/examples/after.md +0 -0
  189. /package/{using-asyncio-python → skills/using-asyncio-python}/examples/before.md +0 -0
  190. /package/{using-asyncio-python → skills/using-asyncio-python}/references/api_reference.md +0 -0
  191. /package/{using-asyncio-python → skills/using-asyncio-python}/references/review-checklist.md +0 -0
  192. /package/{using-asyncio-python → skills/using-asyncio-python}/scripts/check_blocking.py +0 -0
  193. /package/{using-asyncio-python → skills/using-asyncio-python}/scripts/example.py +0 -0
  194. /package/{web-scraping-python → skills/web-scraping-python}/SKILL.md +0 -0
  195. /package/{web-scraping-python → skills/web-scraping-python}/assets/example_asset.txt +0 -0
  196. /package/{web-scraping-python → skills/web-scraping-python}/evals/evals.json +0 -0
  197. /package/{web-scraping-python → skills/web-scraping-python}/examples/after.md +0 -0
  198. /package/{web-scraping-python → skills/web-scraping-python}/examples/before.md +0 -0
  199. /package/{web-scraping-python → skills/web-scraping-python}/references/api_reference.md +0 -0
  200. /package/{web-scraping-python → skills/web-scraping-python}/references/review-checklist.md +0 -0
  201. /package/{web-scraping-python → skills/web-scraping-python}/scripts/example.py +0 -0
  202. /package/{web-scraping-python → skills/web-scraping-python}/scripts/new_scraper.py +0 -0
@@ -0,0 +1,290 @@
1
+ ---
2
+ name: rust-in-action
3
+ description: >
4
+ Write and review Rust code using systems programming concepts from "Rust in Action"
5
+ by Tim McNamara. Covers language foundations, ownership and borrowing, smart pointers,
6
+ data representation (bits, endianness, floats), memory (stack/heap/virtual), file I/O,
7
+ networking (TCP/HTTP), concurrency (threads/closures), and OS-level programming.
8
+ Use when writing systems-level Rust, working with binary data, raw pointers, file formats,
9
+ network protocols, or low-level memory. Trigger on: "Rust", "systems programming",
10
+ "smart pointers", "endianness", "bit manipulation", "TCP", "raw pointers", "serde",
11
+ "borrow checker", "ownership", "concurrency", "kernel", ".rs files", "cargo".
12
+ ---
13
+
14
+ # Rust in Action Skill
15
+
16
+ Apply the systems programming practices from Tim McNamara's "Rust in Action" to review existing code and write new Rust. This skill operates in two modes: **Review Mode** (analyze code for violations of Rust idioms and systems programming correctness) and **Write Mode** (produce safe, idiomatic, systems-capable Rust from scratch).
17
+
18
+ The key differentiator of this book: Rust is taught through real systems — a CPU simulator, key-value store, NTP client, raw TCP stack, and OS kernel. Practices focus on correctness at the hardware boundary, not just language syntax.
19
+
20
+ ## Reference Files
21
+
22
+ - `practices-catalog.md` — Before/after examples for ownership, smart pointers, bit ops, I/O, networking, concurrency, error wrapping, and state machines
23
+
24
+ ## How to Use This Skill
25
+
26
+ **Before responding**, read `practices-catalog.md` for the topic at hand. For ownership/borrowing issues read the ownership section. For systems/binary data read the data section. For a full review, read all sections.
27
+
28
+ ---
29
+
30
+ ## Mode 1: Code Review
31
+
32
+ When the user asks you to **review** Rust code, follow this process:
33
+
34
+ ### Step 1: Identify the Domain
35
+ Determine whether the code is application-level, systems-level (binary data, I/O, networking, memory), or concurrent. The review focus shifts accordingly.
36
+
37
+ ### Step 2: Analyze the Code
38
+
39
+ Check these areas in order of severity:
40
+
41
+ 1. **Ownership & Borrowing** (Ch 4): Unnecessary `.clone()`? Value moved when a borrow would suffice? Use references where full ownership is not required.
42
+ 2. **Smart Pointer Choice** (Ch 6): Is the right pointer type used? `Box<T>` for heap, `Rc<T>` for single-thread shared, `Arc<T>` for multi-thread shared, `RefCell<T>` for interior mutability (single-thread), `Mutex<T>` for interior mutability (multi-thread). `Cow<T>` when data is usually read but occasionally mutated.
43
+ 3. **Error Handling** (Ch 3, 8): `.unwrap()` or `.expect()` where `?` belongs? For library code, define a custom error type that wraps downstream errors via `From` impl. Never leak internal error types across the public API boundary.
44
+ 4. **Binary Data & Endianness** (Ch 5, 7): Are integer byte representations explicit? Use `to_le_bytes()` / `from_le_bytes()` / `to_be_bytes()`. Validate with checksums when writing binary formats. Use `serde` + `bincode` for structured serialization.
45
+ 5. **Memory** (Ch 6): Is `unsafe` minimized? Raw pointer use must be bounded by a safe abstraction. Stack vs heap allocation: prefer stack; use `Box` only when size is unknown at compile time or you need heap lifetime.
46
+ 6. **File & I/O** (Ch 7): Use `BufReader`/`BufWriter` for large files. Handle `ENOENT`, `EPERM`, `ENOSPC` distinctly — don't collapse I/O errors to strings. Use `std::fs::Path` for type-safe path handling.
47
+ 7. **Networking** (Ch 8): TCP state is implicit in OS — model explicit state machines with enums. Use trait objects (`Box<dyn Trait>`) only when heterogeneous runtime dispatch is needed. Prefer `impl Trait` for static dispatch.
48
+ 8. **Concurrency** (Ch 10): Closures passed to threads must be `'static` or use `move`. Shared mutable state needs `Arc<Mutex<T>>`. Use channels for message passing over shared state. Thread pool patterns over spawning one thread per task.
49
+ 9. **Time** (Ch 9): Don't use `std::time::SystemTime` for elapsed measurement — it can go backwards. Use `std::time::Instant` for durations. For network time, NTP requires epoch conversion (NTP epoch: 1900 vs Unix: 1970 — offset 70 years = 2_208_988_800 seconds).
50
+ 10. **Idioms**: Iterator adapters over manual loops. `for item in &collection` not `for i in 0..collection.len()`. `if let`/`while let` for single-variant matching. Exhaustive `match` — no silent wildcard arms.
51
+
52
+ ### Step 3: Report Findings
53
+ For each issue, report:
54
+ - **Chapter reference** (e.g., "Ch 6: Smart Pointers")
55
+ - **Location** in the code
56
+ - **What's wrong** (the anti-pattern)
57
+ - **How to fix it** (the idiomatic / systems-correct approach)
58
+ - **Priority**: Critical (safety/UB/data corruption), Important (idiom/correctness), Suggestion (polish)
59
+
60
+ ### Step 4: Provide Fixed Code
61
+ Offer a corrected version with comments explaining each change.
62
+
63
+ ---
64
+
65
+ ## Mode 2: Writing New Code
66
+
67
+ When the user asks you to **write** new Rust code, apply these core principles:
68
+
69
+ ### Language Foundations (Ch 2)
70
+
71
+ 1. **Use cargo, not rustc directly** (Ch 2). `cargo new`, `cargo build`, `cargo test`, `cargo doc`. Add third-party crates via `Cargo.toml` — never manually link.
72
+
73
+ 2. **Prefer integer types that match the domain** (Ch 2). Use `u8` for bytes, `u16`/`u32`/`u64` for protocol fields sized to spec, `i64` for timestamps. Avoid default `usize` for domain values.
74
+
75
+ 3. **Use `loop` for retry/event loops; `while` for condition-driven; `for` for iteration** (Ch 2). Never use `loop { if cond { break } }` where `while cond {}` is clearer.
76
+
77
+ ### Compound Types & Traits (Ch 3)
78
+
79
+ 4. **Model domain state with enums, not stringly-typed flags** (Ch 3). Enums with data (`enum Packet { Ack(u32), Data(Vec<u8>) }`) replace boolean + optional pairs and make invalid states unrepresentable.
80
+
81
+ 5. **Implement `new()` as the canonical constructor** (Ch 3). `impl MyStruct { pub fn new(...) -> Self { ... } }`. Use `Default` for zero-value construction.
82
+
83
+ 6. **Implement `std::fmt::Display` for user-facing output, `Debug` via derive** (Ch 3). Derive `Debug`; hand-implement `Display`. Never use `{:?}` in user-facing messages.
84
+
85
+ 7. **Use `pub(crate)` to limit visibility to the crate; keep internals private** (Ch 3). Public API surface should be minimal and intentional.
86
+
87
+ 8. **Document public items with `///` rustdoc comments** (Ch 3). Include examples in doc comments — `cargo test` runs them.
88
+
89
+ ### Ownership, Borrowing & Smart Pointers (Ch 4, 6)
90
+
91
+ 9. **Use references where full ownership is not required** (Ch 4). Pass `&T` for read, `&mut T` for write. Only transfer ownership when the callee must own (e.g., storing in a struct).
92
+
93
+ 10. **Choose smart pointers by use case** (Ch 6):
94
+ - `Box<T>` — heap allocation, single owner, unknown size at compile time
95
+ - `Rc<T>` — shared ownership, single-threaded
96
+ - `Arc<T>` — shared ownership, multi-threaded
97
+ - `Cell<T>` — interior mutability for `Copy` types, single-threaded
98
+ - `RefCell<T>` — interior mutability for non-`Copy`, single-threaded, runtime borrow checks
99
+ - `Cow<'a, T>` — clone-on-write, avoids allocation when data is only read
100
+ - `Arc<Mutex<T>>` — shared mutable state across threads
101
+
102
+ 11. **Never use `Rc` across thread boundaries** (Ch 6). The compiler enforces this — `Rc` is not `Send`. Use `Arc` instead.
103
+
104
+ 12. **Minimize `unsafe` blocks; wrap them in safe abstractions** (Ch 6). Raw pointers (`*const T`, `*mut T`) must be bounded within a module or function that upholds safety invariants. Document the safety contract with `// SAFETY:` comments.
105
+
106
+ ### Data Representation (Ch 5)
107
+
108
+ 13. **Be explicit about endianness in binary protocols** (Ch 5, 7). Use `u32::to_le_bytes()`, `u32::from_be_bytes()` etc. Never assume native endianness when writing to disk or network.
109
+
110
+ 14. **Use bit operations to inspect and build packed data** (Ch 5). AND (`&`) to isolate bits, OR (`|`) to set bits, shift (`<<`, `>>`) to position. Use named constants for masks: `const SIGN_BIT: u32 = 0x8000_0000`.
111
+
112
+ 15. **Validate binary data with checksums** (Ch 7). For key-value stores and file formats, store a CRC or hash alongside data. Verify on read before trusting.
113
+
114
+ ### Files & Storage (Ch 7)
115
+
116
+ 16. **Use `BufReader`/`BufWriter` for file I/O** (Ch 7). Raw `File::read()` makes a syscall per call. `BufReader` batches reads into user-space buffer.
117
+
118
+ 17. **Use `serde` + `bincode` for binary serialization** (Ch 7). Add `#[derive(Serialize, Deserialize)]`; let `bincode::serialize`/`deserialize` handle encoding. Use `serde_json` for human-readable formats.
119
+
120
+ 18. **Use `std::path::Path` and `PathBuf` for file paths** (Ch 7). Never build paths with string concatenation. Use `path.join()`, `path.extension()`, `path.file_name()`.
121
+
122
+ ### Networking (Ch 8)
123
+
124
+ 19. **Model protocol state explicitly with enums** (Ch 8). A TCP connection has states (SYN_SENT, ESTABLISHED, CLOSE_WAIT, etc.). Encode them as enum variants — the compiler enforces valid transitions.
125
+
126
+ 20. **Wrap library errors in a domain error type** (Ch 8). When a function calls multiple libraries (network + I/O + parse), define an enum that wraps each. Implement `From<LibError> for DomainError` so `?` converts automatically.
127
+
128
+ 21. **Use trait objects only for heterogeneous runtime dispatch** (Ch 8). `Vec<Box<dyn Animal>>` is correct when you have a mixed collection. For a single concrete type, `impl Trait` is zero-cost.
129
+
130
+ ### Concurrency (Ch 10)
131
+
132
+ 22. **Use `move` closures when passing to threads** (Ch 10). `thread::spawn(move || { ... })` transfers ownership of captured variables into the thread. This is required when the closure outlives the current stack frame.
133
+
134
+ 23. **Use `Arc::clone()` explicitly, not `.clone()` on a value** (Ch 10). `Arc::clone(&ptr)` is idiomatic — it's cheap (increments a reference count). Avoid `.clone()` on the inner value.
135
+
136
+ 24. **Use channels for work distribution; `Arc<Mutex<T>>` for shared state** (Ch 10). Channels (`std::sync::mpsc`) are simpler and safer. Use shared state only when channels don't fit (e.g., result collection).
137
+
138
+ 25. **Use thread pools over raw `thread::spawn` per task** (Ch 10). Spawning one thread per request doesn't scale. Use `rayon`, `tokio`, or a manual pool with a bounded queue.
139
+
140
+ ### Time (Ch 9)
141
+
142
+ 26. **Use `Instant` for elapsed time, `SystemTime` for wall clock** (Ch 9). `SystemTime` can go backwards (NTP adjustments, leap seconds). `Instant` is monotonic.
143
+
144
+ 27. **Apply the NTP epoch offset when working with network time** (Ch 9). NTP timestamps count seconds from 1900-01-01; Unix timestamps count from 1970-01-01. Offset: `2_208_988_800u64` seconds.
145
+
146
+ ---
147
+
148
+ ## Smart Pointer Selection Guide (Ch 6)
149
+
150
+ ```
151
+ Is the data shared across threads?
152
+ ├── Yes → Arc<T> (read-only) or Arc<Mutex<T>> (mutable)
153
+ └── No
154
+ ├── Shared (multiple owners, single thread)?
155
+ │ └── Rc<T> (read-only) or Rc<RefCell<T>> (mutable)
156
+ └── Single owner
157
+ ├── Size unknown at compile time / recursive type?
158
+ │ └── Box<T>
159
+ ├── Usually read, occasionally cloned/modified?
160
+ │ └── Cow<'a, T>
161
+ └── Interior mutability needed?
162
+ ├── Copy type → Cell<T>
163
+ └── Non-Copy → RefCell<T>
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Code Structure Templates
169
+
170
+ ### Binary Protocol Field (Ch 5, 7)
171
+ ```rust
172
+ /// Parse a 4-byte big-endian u32 from a byte buffer at offset.
173
+ fn read_u32_be(buf: &[u8], offset: usize) -> Result<u32, ParseError> {
174
+ buf.get(offset..offset + 4)
175
+ .ok_or(ParseError::UnexpectedEof)
176
+ .map(|b| u32::from_be_bytes(b.try_into().unwrap()))
177
+ }
178
+
179
+ const FLAGS_MASK: u8 = 0b0000_1111; // isolate lower 4 bits
180
+ fn extract_flags(byte: u8) -> u8 {
181
+ byte & FLAGS_MASK
182
+ }
183
+ ```
184
+
185
+ ### Library Error Type (Ch 8)
186
+ ```rust
187
+ #[derive(Debug)]
188
+ pub enum AppError {
189
+ Io(std::io::Error),
190
+ Network(std::net::AddrParseError),
191
+ Parse(String),
192
+ }
193
+
194
+ impl std::fmt::Display for AppError {
195
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196
+ match self {
197
+ AppError::Io(e) => write!(f, "I/O error: {e}"),
198
+ AppError::Network(e) => write!(f, "network error: {e}"),
199
+ AppError::Parse(msg) => write!(f, "parse error: {msg}"),
200
+ }
201
+ }
202
+ }
203
+
204
+ impl std::error::Error for AppError {}
205
+ impl From<std::io::Error> for AppError {
206
+ fn from(e: std::io::Error) -> Self { AppError::Io(e) }
207
+ }
208
+ impl From<std::net::AddrParseError> for AppError {
209
+ fn from(e: std::net::AddrParseError) -> Self { AppError::Network(e) }
210
+ }
211
+ ```
212
+
213
+ ### State Machine with Enum (Ch 8)
214
+ ```rust
215
+ #[derive(Debug, Clone, PartialEq)]
216
+ enum ConnectionState {
217
+ Idle,
218
+ Connecting { addr: std::net::SocketAddr },
219
+ Connected { stream: std::net::TcpStream },
220
+ Closed,
221
+ }
222
+
223
+ impl ConnectionState {
224
+ fn connect(addr: std::net::SocketAddr) -> Result<Self, AppError> {
225
+ let stream = std::net::TcpStream::connect(addr)?;
226
+ Ok(ConnectionState::Connected { stream })
227
+ }
228
+ }
229
+ ```
230
+
231
+ ### Thread Pool Pattern (Ch 10)
232
+ ```rust
233
+ use std::sync::{Arc, Mutex};
234
+ use std::sync::mpsc;
235
+ use std::thread;
236
+
237
+ type Job = Box<dyn FnOnce() + Send + 'static>;
238
+
239
+ struct ThreadPool {
240
+ sender: mpsc::Sender<Job>,
241
+ }
242
+
243
+ impl ThreadPool {
244
+ fn new(size: usize) -> Self {
245
+ let (sender, receiver) = mpsc::channel::<Job>();
246
+ let receiver = Arc::new(Mutex::new(receiver));
247
+
248
+ for _ in 0..size {
249
+ let rx = Arc::clone(&receiver);
250
+ thread::spawn(move || loop {
251
+ let job = rx.lock().expect("mutex poisoned").recv();
252
+ match job {
253
+ Ok(f) => f(),
254
+ Err(_) => break, // channel closed
255
+ }
256
+ });
257
+ }
258
+ ThreadPool { sender }
259
+ }
260
+
261
+ fn execute(&self, f: impl FnOnce() + Send + 'static) {
262
+ self.sender.send(Box::new(f)).expect("thread pool closed");
263
+ }
264
+ }
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Priority of Practices by Impact
270
+
271
+ ### Critical (Safety, Correctness, UB)
272
+ - Ch 4: Borrow, don't clone — never move when a reference suffices
273
+ - Ch 6: Choose the right smart pointer — `Rc` is not thread-safe; don't share across threads
274
+ - Ch 6: Wrap `unsafe` in safe abstractions — document safety contracts with `// SAFETY:`
275
+ - Ch 5/7: Explicit endianness — wrong byte order silently corrupts binary data
276
+ - Ch 9: Use `Instant` for elapsed time — `SystemTime` can go backwards
277
+
278
+ ### Important (Idiom & Maintainability)
279
+ - Ch 3: Domain enums over stringly-typed state — invalid states should not compile
280
+ - Ch 8: Wrap downstream errors in a domain error type with `From` impls
281
+ - Ch 8: `impl Trait` over `dyn Trait` when types are homogeneous
282
+ - Ch 10: `move` closures for threads — required when closure outlives the stack frame
283
+ - Ch 10: `Arc::clone()` idiom — makes cheap pointer clone explicit
284
+
285
+ ### Suggestions (Systems Polish)
286
+ - Ch 2: Size integer types to the protocol spec — `u8` for bytes, `u16` for ports
287
+ - Ch 5: Named bit-mask constants — `const SIGN_BIT: u32 = 0x8000_0000`
288
+ - Ch 7: `BufReader`/`BufWriter` for all file I/O — syscall batching
289
+ - Ch 7: Checksums on binary writes — detect corruption on read
290
+ - Ch 9: NTP epoch offset constant — `const NTP_UNIX_OFFSET: u64 = 2_208_988_800`
@@ -0,0 +1,38 @@
1
+ {
2
+ "evals": [
3
+ {
4
+ "id": "eval-01-endianness-unwrap-static-mut",
5
+ "prompt": "Review this Rust code:\n\n```rust\nstatic mut PACKET_COUNT: u32 = 0;\n\nfn parse_header(bytes: &[u8]) -> u32 {\n let id = u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);\n unsafe { PACKET_COUNT += 1; }\n id\n}\n\nfn read_data(path: &str) -> Vec<u8> {\n std::fs::read(path).unwrap()\n}\n\nfn write_id(id: u32) -> Vec<u8> {\n id.to_ne_bytes().to_vec()\n}\n```",
6
+ "expectations": [
7
+ "Flag Ch 5/7: from_ne_bytes uses native endianness — silently produces wrong results on big-endian hosts; use from_le_bytes or from_be_bytes to match the protocol spec",
8
+ "Flag Ch 6/10: static mut PACKET_COUNT is a data race in concurrent code — replace with Arc<Mutex<u32>> or an atomic (std::sync::atomic::AtomicU32)",
9
+ "Flag Ch 3: .unwrap() in read_data will panic on I/O error — return Result<Vec<u8>, std::io::Error> and use ?",
10
+ "Flag Ch 7: to_ne_bytes in write_id — same endianness problem as parse_header; must match the parse side",
11
+ "Note that parse_header does no bounds checking before indexing bytes[0..3] — should use bytes.get(0..4).ok_or(...) to avoid a panic on short input",
12
+ "Provide corrected versions using from_le_bytes/to_le_bytes, AtomicU32 or Arc<Mutex<u32>>, and Result"
13
+ ]
14
+ },
15
+ {
16
+ "id": "eval-02-smart-pointer-choice",
17
+ "prompt": "Review this Rust code:\n\n```rust\nuse std::rc::Rc;\nuse std::thread;\n\nstruct Cache {\n data: Rc<Vec<String>>,\n}\n\nimpl Cache {\n fn spawn_reader(&self) {\n let d = Rc::clone(&self.data);\n thread::spawn(move || {\n println!(\"{:?}\", d);\n });\n }\n}\n\nfn make_buffer(large: bool) -> Box<Vec<u8>> {\n if large {\n Box::new(vec![0u8; 1024 * 1024])\n } else {\n Box::new(vec![0u8; 64])\n }\n}\n```",
18
+ "expectations": [
19
+ "Flag Ch 6: Rc is not Send — sharing Rc across thread boundaries is a compile error; replace Rc<Vec<String>> with Arc<Vec<String>>",
20
+ "Flag Ch 6: Box<Vec<u8>> is redundant — Vec<u8> already heap-allocates; Box<Vec<T>> is a double-indirection with no benefit; return Vec<u8> directly",
21
+ "Note that Arc::clone(&self.data) is the correct idiom for incrementing an Arc refcount — it makes the cheap clone explicit",
22
+ "Note that spawn_reader returns no JoinHandle — callers cannot join the thread or detect panics; suggest returning thread::JoinHandle<()>",
23
+ "Provide corrected versions using Arc<Vec<String>>, returning Vec<u8> directly, and returning JoinHandle from spawn_reader"
24
+ ]
25
+ },
26
+ {
27
+ "id": "eval-03-idiomatic-systems-rust",
28
+ "prompt": "Review this Rust code:\n\n```rust\nuse std::sync::{Arc, Mutex};\nuse std::sync::atomic::{AtomicU64, Ordering};\nuse std::path::Path;\nuse std::io::{BufReader, Read};\nuse std::fs::File;\n\n#[derive(Debug)]\npub enum StoreError {\n Io(std::io::Error),\n Corrupt { offset: usize },\n}\n\nimpl std::fmt::Display for StoreError {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n match self {\n StoreError::Io(e) => write!(f, \"io: {e}\"),\n StoreError::Corrupt { offset } => write!(f, \"corrupt at byte {offset}\"),\n }\n }\n}\n\nimpl std::error::Error for StoreError {}\nimpl From<std::io::Error> for StoreError {\n fn from(e: std::io::Error) -> Self { StoreError::Io(e) }\n}\n\nstruct Store {\n reads: AtomicU64,\n data: Arc<Mutex<Vec<u8>>>,\n}\n\nimpl Store {\n fn new() -> Self {\n Store { reads: AtomicU64::new(0), data: Arc::new(Mutex::new(vec![])) }\n }\n\n fn load(&self, path: &Path) -> Result<(), StoreError> {\n let f = File::open(path)?;\n let mut reader = BufReader::new(f);\n let mut buf = vec![];\n reader.read_to_end(&mut buf)?;\n *self.data.lock().expect(\"mutex poisoned\") = buf;\n Ok(())\n }\n\n fn read(&self, offset: usize, len: usize) -> Result<Vec<u8>, StoreError> {\n self.reads.fetch_add(1, Ordering::Relaxed);\n let data = self.data.lock().expect(\"mutex poisoned\");\n data.get(offset..offset + len)\n .map(|s| s.to_vec())\n .ok_or(StoreError::Corrupt { offset })\n }\n}\n```",
29
+ "expectations": [
30
+ "Recognize this code is idiomatic systems Rust — do NOT manufacture issues",
31
+ "Acknowledge: StoreError with Display + Error + From<io::Error> for ? propagation (Ch 3, 8); BufReader for batched I/O (Ch 7); &Path parameter (Ch 7); Arc<Mutex<T>> for shared mutable data (Ch 6, 10); AtomicU64 for lock-free counter (Ch 10); .expect('mutex poisoned') with reason (Ch 10)",
32
+ "At most suggest: reads counter could use Ordering::SeqCst if cross-thread visibility of exact count matters; or note that Relaxed is fine for approximate telemetry",
33
+ "At most suggest: Default could be derived or implemented for Store alongside new(), enabling Store::default() construction",
34
+ "Do NOT flag AtomicU64 with Relaxed ordering as a bug — it is appropriate for a read counter where exact ordering across threads is not required"
35
+ ]
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,156 @@
1
+ # After: Rust in Action
2
+
3
+ The same utility rewritten with idiomatic systems Rust — explicit endianness, buffered I/O, a domain error type, checksum validation, thread pool, and safe shared state.
4
+
5
+ ```rust
6
+ use std::fmt;
7
+ use std::fs::File;
8
+ use std::io::{BufReader, Read};
9
+ use std::path::Path;
10
+ use std::sync::{Arc, Mutex};
11
+ use std::sync::mpsc;
12
+ use std::thread;
13
+
14
+ // Domain error type — wraps all downstream errors (Ch 3, 8)
15
+ #[derive(Debug)]
16
+ pub enum LogError {
17
+ Io(std::io::Error),
18
+ InvalidRecord { offset: usize, reason: &'static str },
19
+ ChecksumMismatch { expected: u32, got: u32 },
20
+ }
21
+
22
+ impl fmt::Display for LogError {
23
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24
+ match self {
25
+ LogError::Io(e) => write!(f, "I/O: {e}"),
26
+ LogError::InvalidRecord { offset, reason } =>
27
+ write!(f, "invalid record at {offset}: {reason}"),
28
+ LogError::ChecksumMismatch { expected, got } =>
29
+ write!(f, "checksum mismatch: expected {expected:#010x}, got {got:#010x}"),
30
+ }
31
+ }
32
+ }
33
+
34
+ impl std::error::Error for LogError {}
35
+ impl From<std::io::Error> for LogError {
36
+ fn from(e: std::io::Error) -> Self { LogError::Io(e) }
37
+ }
38
+
39
+ // BufReader for batched I/O syscalls; &Path for type-safe path (Ch 7)
40
+ // Returns Result — caller decides how to handle failure (Ch 3)
41
+ fn read_log(path: &Path) -> Result<Vec<u8>, LogError> {
42
+ let file = File::open(path)?; // ? converts io::Error → LogError (Ch 8)
43
+ let mut reader = BufReader::new(file); // batched reads (Ch 7)
44
+ let mut buf = Vec::new();
45
+ reader.read_to_end(&mut buf)?;
46
+ Ok(buf)
47
+ }
48
+
49
+ // Explicit little-endian — deterministic across all hosts (Ch 5, 7)
50
+ fn parse_record_id(bytes: &[u8], offset: usize) -> Result<u32, LogError> {
51
+ bytes.get(offset..offset + 4)
52
+ .ok_or(LogError::InvalidRecord { offset, reason: "not enough bytes for id" })
53
+ .map(|b| u32::from_le_bytes(b.try_into().unwrap()))
54
+ }
55
+
56
+ // Simulate a simple CRC32-like checksum (Ch 7)
57
+ fn checksum(data: &[u8]) -> u32 {
58
+ data.iter().fold(0u32, |acc, &b| acc.wrapping_add(b as u32))
59
+ }
60
+
61
+ // Explicit little-endian write + checksum (Ch 5, 7)
62
+ fn write_record(id: u32, data: &[u8]) -> Vec<u8> {
63
+ let mut out = Vec::with_capacity(4 + data.len() + 4);
64
+ out.extend_from_slice(&id.to_le_bytes()); // SAFETY: LE is the protocol spec
65
+ out.extend_from_slice(data);
66
+ let crc = checksum(&out);
67
+ out.extend_from_slice(&crc.to_le_bytes()); // append checksum
68
+ out
69
+ }
70
+
71
+ fn read_record(buf: &[u8]) -> Result<(u32, &[u8]), LogError> {
72
+ let id = parse_record_id(buf, 0)?;
73
+ let payload = buf.get(4..buf.len() - 4)
74
+ .ok_or(LogError::InvalidRecord { offset: 4, reason: "too short for payload + checksum" })?;
75
+ let stored_crc = u32::from_le_bytes(buf[buf.len() - 4..].try_into().unwrap());
76
+ let computed = checksum(&buf[..buf.len() - 4]);
77
+ if computed != stored_crc {
78
+ return Err(LogError::ChecksumMismatch { expected: stored_crc, got: computed });
79
+ }
80
+ Ok((id, payload))
81
+ }
82
+
83
+ // Thread pool via channel — no unbounded thread spawning (Ch 10)
84
+ type Job = Box<dyn FnOnce() + Send + 'static>;
85
+
86
+ struct Pool {
87
+ tx: mpsc::Sender<Job>,
88
+ }
89
+
90
+ impl Pool {
91
+ fn new(workers: usize) -> Self {
92
+ let (tx, rx) = mpsc::channel::<Job>();
93
+ let rx = Arc::new(Mutex::new(rx));
94
+ for _ in 0..workers {
95
+ let rx = Arc::clone(&rx);
96
+ thread::spawn(move || loop {
97
+ match rx.lock().expect("mutex poisoned").recv() {
98
+ Ok(job) => job(),
99
+ Err(_) => break, // sender dropped — shut down
100
+ }
101
+ });
102
+ }
103
+ Pool { tx }
104
+ }
105
+
106
+ fn submit(&self, job: impl FnOnce() + Send + 'static) {
107
+ self.tx.send(Box::new(job)).expect("pool closed");
108
+ }
109
+ }
110
+
111
+ // Safe shared error counter via Arc<Mutex<T>> (Ch 6, 10)
112
+ fn make_error_counter() -> Arc<Mutex<u32>> {
113
+ Arc::new(Mutex::new(0))
114
+ }
115
+
116
+ fn record_error(counter: &Arc<Mutex<u32>>) {
117
+ *counter.lock().expect("mutex poisoned") += 1;
118
+ }
119
+
120
+ fn main() -> Result<(), LogError> {
121
+ let path = Path::new("data.log");
122
+ let bytes = read_log(path)?;
123
+
124
+ let id = parse_record_id(&bytes, 0)?;
125
+ println!("id: {id}");
126
+
127
+ let pool = Pool::new(4);
128
+ let errors = make_error_counter();
129
+
130
+ // move — closure owns record + error counter clone (Ch 10)
131
+ let record = bytes.clone();
132
+ let err_counter = Arc::clone(&errors);
133
+ pool.submit(move || {
134
+ println!("processing {} bytes, id={}", record.len(), id);
135
+ if record.len() < 8 {
136
+ record_error(&err_counter);
137
+ }
138
+ });
139
+
140
+ // Give threads time to finish (production code would use join handles)
141
+ std::thread::sleep(std::time::Duration::from_millis(10));
142
+ println!("errors: {}", errors.lock().unwrap());
143
+
144
+ Ok(())
145
+ }
146
+ ```
147
+
148
+ **Key improvements:**
149
+ - `LogError` wraps all downstream errors with `From` impls — `?` converts automatically (Ch 3, 8)
150
+ - `BufReader` batches file I/O syscalls — essential for large files (Ch 7)
151
+ - `u32::from_le_bytes()` / `to_le_bytes()` — explicit endianness, correct across all hosts (Ch 5)
152
+ - Checksum appended and verified on read — corruption is detectable (Ch 7)
153
+ - Thread pool via `mpsc::channel` + `Arc<Mutex<Receiver>>` — bounded concurrency (Ch 10)
154
+ - `Arc<Mutex<u32>>` replaces `unsafe static mut` — safe shared mutable state (Ch 6, 10)
155
+ - `move` closures transfer ownership into threads — required for `'static` bound (Ch 10)
156
+ - `&Path` instead of `String` for file path — type-safe, works with literals (Ch 7)
@@ -0,0 +1,56 @@
1
+ # Before: Rust in Action
2
+
3
+ A systems utility that reads a binary log file, parses records, and processes them concurrently — with common systems-level anti-patterns.
4
+
5
+ ```rust
6
+ use std::fs::File;
7
+ use std::io::Read;
8
+ use std::thread;
9
+
10
+ // Silently panics on any I/O failure; no error type
11
+ fn read_log(path: String) -> Vec<u8> {
12
+ let mut file = File::open(path).unwrap();
13
+ let mut buf = vec![];
14
+ file.read_to_end(&mut buf).unwrap();
15
+ buf
16
+ }
17
+
18
+ // Assumes native endianness — corrupts data on big-endian hosts
19
+ fn parse_record_id(bytes: &[u8]) -> u32 {
20
+ let arr = [bytes[0], bytes[1], bytes[2], bytes[3]];
21
+ u32::from_ne_bytes(arr) // native endian — wrong for protocol
22
+ }
23
+
24
+ // Spawns one thread per record — no pooling
25
+ fn process_all(records: Vec<Vec<u8>>) {
26
+ for record in records {
27
+ thread::spawn(|| {
28
+ println!("processing {} bytes", record.len());
29
+ });
30
+ }
31
+ // No join — threads may not finish before main exits
32
+ }
33
+
34
+ // Shared mutable state with unsafe global
35
+ static mut ERROR_COUNT: u32 = 0;
36
+
37
+ fn record_error() {
38
+ unsafe {
39
+ ERROR_COUNT += 1; // data race — undefined behavior
40
+ }
41
+ }
42
+
43
+ // No endianness comment, no checksum, no error type
44
+ fn write_record(id: u32, data: &[u8]) -> Vec<u8> {
45
+ let mut out = vec![];
46
+ out.extend_from_slice(&id.to_ne_bytes()); // native endian — wrong
47
+ out.extend_from_slice(data);
48
+ out // no checksum — corruption undetectable
49
+ }
50
+
51
+ fn main() {
52
+ let bytes = read_log(String::from("data.log"));
53
+ let id = parse_record_id(&bytes);
54
+ println!("id: {}", id);
55
+ }
56
+ ```