agy-superpowers 5.1.3 → 5.1.5

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 (188) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +189 -186
  3. package/package.json +1 -1
  4. package/template/agent/patches/skills-patches.md +24 -0
  5. package/template/agent/rules/git-policy.md +25 -0
  6. package/template/agent/skills/finishing-a-development-branch/SKILL.md +18 -6
  7. package/template/agent/skills/rust-developer/SKILL.md +281 -0
  8. package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
  9. package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
  10. package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
  11. package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
  12. package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
  13. package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
  14. package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
  15. package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
  16. package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
  17. package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
  18. package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
  19. package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
  20. package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
  21. package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
  22. package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
  23. package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
  24. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
  25. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
  26. package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
  27. package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
  28. package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
  29. package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
  30. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
  31. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
  32. package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
  33. package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
  34. package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
  35. package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
  36. package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
  37. package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
  38. package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
  39. package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
  40. package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
  41. package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
  42. package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
  43. package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
  44. package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
  45. package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
  46. package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
  47. package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
  48. package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
  49. package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
  50. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
  51. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
  52. package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
  53. package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
  54. package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
  55. package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
  56. package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
  57. package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
  58. package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
  59. package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
  60. package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
  61. package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
  62. package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
  63. package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
  64. package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
  65. package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
  66. package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
  67. package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
  68. package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
  69. package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
  70. package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
  71. package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
  72. package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
  73. package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
  74. package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
  75. package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
  76. package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
  77. package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
  78. package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
  79. package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
  80. package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
  81. package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
  82. package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
  83. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
  84. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
  85. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
  86. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
  87. package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
  88. package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
  89. package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
  90. package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
  91. package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
  92. package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
  93. package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
  94. package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
  95. package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
  96. package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
  97. package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
  98. package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
  99. package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
  100. package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
  101. package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
  102. package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
  103. package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
  104. package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
  105. package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
  106. package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
  107. package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
  108. package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
  109. package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
  110. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
  111. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
  112. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
  113. package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
  114. package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
  115. package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
  116. package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
  117. package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
  118. package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
  119. package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
  120. package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
  121. package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
  122. package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
  123. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
  124. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
  125. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
  126. package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
  127. package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
  128. package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
  129. package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
  130. package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
  131. package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
  132. package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
  133. package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
  134. package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
  135. package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
  136. package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
  137. package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
  138. package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
  139. package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
  140. package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
  141. package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
  142. package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
  143. package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
  144. package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
  145. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
  146. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
  147. package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
  148. package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
  149. package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
  150. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
  151. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
  152. package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
  153. package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
  154. package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
  155. package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
  156. package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
  157. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
  158. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
  159. package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
  160. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
  161. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
  162. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
  163. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
  164. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
  165. package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
  166. package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
  167. package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
  168. package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
  169. package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
  170. package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
  171. package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
  172. package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
  173. package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
  174. package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
  175. package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
  176. package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
  177. package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
  178. package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
  179. package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
  180. package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
  181. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
  182. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
  183. package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
  184. package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
  185. package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
  186. package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
  187. package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
  188. package/template/agent/skills/using-git-worktrees/SKILL.md +3 -1
@@ -0,0 +1,125 @@
1
+ # api-must-use
2
+
3
+ > Mark types and functions with `#[must_use]` when ignoring results is likely a bug
4
+
5
+ ## Why It Matters
6
+
7
+ Some return values should never be ignored—`Result`, locks, RAII guards, computed values that have no side effects. Without `#[must_use]`, silently discarding these values can introduce subtle bugs that are hard to detect. The attribute generates compiler warnings when the value is unused.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ // Result ignored - error silently dropped
13
+ fn send_email(to: &str, body: &str) -> Result<(), EmailError> { ... }
14
+
15
+ send_email("user@example.com", "Hello!"); // No warning if Result ignored!
16
+ // Email may have failed, but we don't know
17
+
18
+ // Computed value ignored - likely a bug
19
+ fn compute_checksum(data: &[u8]) -> u32 { ... }
20
+
21
+ let data = vec![1, 2, 3, 4];
22
+ compute_checksum(&data); // Result discarded - pointless call
23
+ ```
24
+
25
+ ## Good
26
+
27
+ ```rust
28
+ #[must_use = "this `Result` may be an `Err` that should be handled"]
29
+ fn send_email(to: &str, body: &str) -> Result<(), EmailError> { ... }
30
+
31
+ send_email("user@example.com", "Hello!");
32
+ // Warning: unused `Result` that must be used
33
+
34
+ // Mark pure functions
35
+ #[must_use = "this returns a new value and does not modify the input"]
36
+ fn compute_checksum(data: &[u8]) -> u32 { ... }
37
+
38
+ compute_checksum(&data);
39
+ // Warning: unused return value of `compute_checksum` that must be used
40
+ ```
41
+
42
+ ## Apply to Types
43
+
44
+ ```rust
45
+ // Mark the type itself when it should always be used
46
+ #[must_use = "futures do nothing unless polled"]
47
+ struct MyFuture<T> { ... }
48
+
49
+ // Mark RAII guards
50
+ #[must_use = "if unused, the lock will be immediately released"]
51
+ struct MutexGuard<'a, T> { ... }
52
+
53
+ // Mark results/errors
54
+ #[must_use = "errors should be handled"]
55
+ enum AppError { ... }
56
+ ```
57
+
58
+ ## Standard Library Examples
59
+
60
+ ```rust
61
+ // Result and Option are #[must_use]
62
+ let v: Vec<i32> = vec![1, 2, 3];
63
+ v.first(); // Warning: unused Option
64
+
65
+ // Iterator adapters are #[must_use]
66
+ v.iter().map(|x| x * 2); // Warning: iterators are lazy
67
+
68
+ // String methods that return new values
69
+ let s = "hello";
70
+ s.to_uppercase(); // Warning: unused String
71
+ ```
72
+
73
+ ## When to Apply
74
+
75
+ ```rust
76
+ // ✅ Pure functions (no side effects)
77
+ #[must_use]
78
+ fn add(a: i32, b: i32) -> i32 { a + b }
79
+
80
+ // ✅ Builder methods returning Self
81
+ #[must_use = "builder methods return a new builder"]
82
+ fn with_timeout(self, t: Duration) -> Self { ... }
83
+
84
+ // ✅ Fallible operations
85
+ #[must_use]
86
+ fn try_parse(s: &str) -> Result<Data, ParseError> { ... }
87
+
88
+ // ✅ Iterators and futures (lazy)
89
+ #[must_use = "iterators are lazy and do nothing unless consumed"]
90
+ struct Map<I, F> { ... }
91
+
92
+ // ❌ Side-effecting functions where result is optional
93
+ fn log(msg: &str) -> Result<(), io::Error> { ... } // Might be ok to ignore
94
+
95
+ // ❌ Methods with useful side effects
96
+ fn vec.push(item); // Mutates vec, no return to use
97
+ ```
98
+
99
+ ## Custom Messages
100
+
101
+ ```rust
102
+ #[must_use = "creating a guard does nothing without assignment"]
103
+ struct ScopeGuard { ... }
104
+
105
+ #[must_use = "this returns the old value"]
106
+ fn replace(&mut self, new: T) -> T { ... }
107
+
108
+ #[must_use = "use `.await` to execute the future"]
109
+ async fn fetch() -> Data { ... }
110
+ ```
111
+
112
+ ## Clippy Lints
113
+
114
+ ```toml
115
+ [lints.clippy]
116
+ must_use_candidate = "warn" # Suggests where to add #[must_use]
117
+ unused_must_use = "deny" # Built-in, treat warnings as errors
118
+ double_must_use = "warn" # Redundant #[must_use]
119
+ ```
120
+
121
+ ## See Also
122
+
123
+ - [api-builder-must-use](./api-builder-must-use.md) - Builder pattern must_use
124
+ - [err-result-over-panic](./err-result-over-panic.md) - Result types require handling
125
+ - [lint-deny-correctness](./lint-deny-correctness.md) - Enabling useful lints
@@ -0,0 +1,162 @@
1
+ # api-newtype-safety
2
+
3
+ > Use newtypes to prevent mixing semantically different values
4
+
5
+ ## Why It Matters
6
+
7
+ Raw primitives like `u64` or `String` carry no semantic meaning. A function taking `(u64, u64)` can easily be called with arguments swapped. Newtypes wrap primitives in distinct types, making the compiler catch mistakes at compile time rather than runtime.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ struct User {
13
+ id: u64,
14
+ group_id: u64,
15
+ created_at: u64, // Unix timestamp
16
+ }
17
+
18
+ fn add_user_to_group(user_id: u64, group_id: u64) { ... }
19
+
20
+ // Bug: arguments swapped - compiles fine, fails at runtime
21
+ let user = User { id: 100, group_id: 5, created_at: 1234567890 };
22
+ add_user_to_group(user.group_id, user.id); // Silent bug!
23
+
24
+ // Bug: wrong field used - timestamp passed as ID
25
+ add_user_to_group(user.created_at, user.group_id); // Compiles fine!
26
+ ```
27
+
28
+ ## Good
29
+
30
+ ```rust
31
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32
+ struct UserId(u64);
33
+
34
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35
+ struct GroupId(u64);
36
+
37
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
38
+ struct Timestamp(u64);
39
+
40
+ struct User {
41
+ id: UserId,
42
+ group_id: GroupId,
43
+ created_at: Timestamp,
44
+ }
45
+
46
+ fn add_user_to_group(user_id: UserId, group_id: GroupId) { ... }
47
+
48
+ // Compile error: expected UserId, found GroupId
49
+ let user = User { ... };
50
+ add_user_to_group(user.group_id, user.id); // Error!
51
+
52
+ // Compile error: expected UserId, found Timestamp
53
+ add_user_to_group(user.created_at, user.group_id); // Error!
54
+ ```
55
+
56
+ ## Derive Common Traits
57
+
58
+ ```rust
59
+ // Minimal: just enough for your use case
60
+ #[derive(Debug, Clone, Copy)]
61
+ struct MeterId(u32);
62
+
63
+ // Full ID type: hashable, comparable, displayable
64
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65
+ struct OrderId(u64);
66
+
67
+ impl std::fmt::Display for OrderId {
68
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69
+ write!(f, "ORD-{:08}", self.0)
70
+ }
71
+ }
72
+
73
+ // With serde for serialization
74
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
75
+ #[serde(transparent)] // Serializes as raw u64
76
+ struct ProductId(u64);
77
+ ```
78
+
79
+ ## Constructor Patterns
80
+
81
+ ```rust
82
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
83
+ struct Email(String);
84
+
85
+ impl Email {
86
+ /// Creates a new Email, validating the format.
87
+ pub fn new(s: &str) -> Result<Self, EmailError> {
88
+ if is_valid_email(s) {
89
+ Ok(Email(s.to_string()))
90
+ } else {
91
+ Err(EmailError::InvalidFormat)
92
+ }
93
+ }
94
+
95
+ /// Returns the email as a string slice.
96
+ pub fn as_str(&self) -> &str {
97
+ &self.0
98
+ }
99
+ }
100
+
101
+ // Usage enforces validation
102
+ let email = Email::new("user@example.com")?; // Must go through validation
103
+ ```
104
+
105
+ ## Zero-Cost Abstraction
106
+
107
+ ```rust
108
+ use std::mem::size_of;
109
+
110
+ #[derive(Clone, Copy)]
111
+ struct Miles(f64);
112
+
113
+ #[derive(Clone, Copy)]
114
+ struct Kilometers(f64);
115
+
116
+ // Same size as raw f64
117
+ assert_eq!(size_of::<Miles>(), size_of::<f64>());
118
+ assert_eq!(size_of::<Kilometers>(), size_of::<f64>());
119
+
120
+ // But can't accidentally mix them
121
+ fn drive(distance: Miles) { ... }
122
+
123
+ let km = Kilometers(100.0);
124
+ drive(km); // Error: expected Miles, found Kilometers
125
+
126
+ // Explicit conversion
127
+ impl From<Kilometers> for Miles {
128
+ fn from(km: Kilometers) -> Self {
129
+ Miles(km.0 * 0.621371)
130
+ }
131
+ }
132
+
133
+ drive(km.into()); // Explicit, visible conversion
134
+ ```
135
+
136
+ ## When Newtypes Help Most
137
+
138
+ ```rust
139
+ // ✅ IDs that could be confused
140
+ fn transfer(from: AccountId, to: AccountId, amount: Money) { ... }
141
+
142
+ // ✅ Units that shouldn't mix
143
+ struct Celsius(f64);
144
+ struct Fahrenheit(f64);
145
+
146
+ // ✅ Validated strings
147
+ struct Username(String); // Validated alphanumeric
148
+ struct Password(String); // Never logged
149
+
150
+ // ✅ Different meanings of same type
151
+ struct Milliseconds(u64);
152
+ struct Seconds(u64);
153
+
154
+ // ❌ Overkill: single use, no confusion possible
155
+ struct X(i32); // Just use i32
156
+ ```
157
+
158
+ ## See Also
159
+
160
+ - [type-newtype-ids](./type-newtype-ids.md) - Newtype pattern for IDs
161
+ - [api-parse-dont-validate](./api-parse-dont-validate.md) - Type-driven validation
162
+ - [own-copy-small](./own-copy-small.md) - Making newtypes Copy
@@ -0,0 +1,177 @@
1
+ # api-non-exhaustive
2
+
3
+ > Use `#[non_exhaustive]` on public enums and structs for forward compatibility
4
+
5
+ ## Why It Matters
6
+
7
+ Adding a variant to a public enum or a field to a public struct is normally a breaking change—downstream code may match exhaustively or use struct literal syntax. `#[non_exhaustive]` forces external code to use wildcards in matches and constructors, allowing you to add variants/fields in minor versions without breaking callers.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ // Public enum - adding variant breaks downstream matches
13
+ pub enum ErrorKind {
14
+ NotFound,
15
+ PermissionDenied,
16
+ TimedOut,
17
+ }
18
+
19
+ // Downstream code
20
+ match error.kind() {
21
+ ErrorKind::NotFound => ...,
22
+ ErrorKind::PermissionDenied => ...,
23
+ ErrorKind::TimedOut => ...,
24
+ // No wildcard - will break when you add ErrorKind::Interrupted
25
+ }
26
+
27
+ // Public struct - adding field breaks downstream construction
28
+ pub struct Config {
29
+ pub name: String,
30
+ pub value: i32,
31
+ }
32
+
33
+ // Downstream code
34
+ let config = Config { name: "test".into(), value: 42 };
35
+ // Will break when you add `pub enabled: bool`
36
+ ```
37
+
38
+ ## Good
39
+
40
+ ```rust
41
+ // Can add variants in minor versions
42
+ #[non_exhaustive]
43
+ pub enum ErrorKind {
44
+ NotFound,
45
+ PermissionDenied,
46
+ TimedOut,
47
+ // Future: can add Interrupted here without breaking changes
48
+ }
49
+
50
+ // Downstream code MUST have wildcard
51
+ match error.kind() {
52
+ ErrorKind::NotFound => ...,
53
+ ErrorKind::PermissionDenied => ...,
54
+ ErrorKind::TimedOut => ...,
55
+ _ => ..., // Required by non_exhaustive
56
+ }
57
+
58
+ // Can add fields in minor versions
59
+ #[non_exhaustive]
60
+ pub struct Config {
61
+ pub name: String,
62
+ pub value: i32,
63
+ }
64
+
65
+ // Downstream CANNOT use struct literal syntax
66
+ // let config = Config { name: "test".into(), value: 42 }; // Error!
67
+
68
+ // Must use constructor
69
+ impl Config {
70
+ pub fn new(name: impl Into<String>, value: i32) -> Self {
71
+ Config { name: name.into(), value }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## How It Works
77
+
78
+ ```rust
79
+ #[non_exhaustive]
80
+ pub enum Status {
81
+ Active,
82
+ Inactive,
83
+ }
84
+
85
+ // Inside your crate: exhaustive match is allowed
86
+ fn internal(s: Status) {
87
+ match s {
88
+ Status::Active => {},
89
+ Status::Inactive => {},
90
+ // No wildcard needed inside defining crate
91
+ }
92
+ }
93
+
94
+ // Outside your crate: wildcard required
95
+ fn external(s: my_crate::Status) {
96
+ match s {
97
+ my_crate::Status::Active => {},
98
+ my_crate::Status::Inactive => {},
99
+ _ => {}, // REQUIRED
100
+ }
101
+ }
102
+ ```
103
+
104
+ ## Struct Usage
105
+
106
+ ```rust
107
+ #[non_exhaustive]
108
+ pub struct Point {
109
+ pub x: f64,
110
+ pub y: f64,
111
+ }
112
+
113
+ impl Point {
114
+ // Provide constructor
115
+ pub fn new(x: f64, y: f64) -> Self {
116
+ Point { x, y }
117
+ }
118
+ }
119
+
120
+ // External code can read fields but not construct with literals
121
+ fn external(p: Point) {
122
+ println!("x: {}, y: {}", p.x, p.y); // Reading is fine
123
+
124
+ // let p2 = Point { x: 1.0, y: 2.0 }; // Error!
125
+ let p2 = Point::new(1.0, 2.0); // Must use constructor
126
+ }
127
+ ```
128
+
129
+ ## Non-Exhaustive Variants
130
+
131
+ ```rust
132
+ pub enum Message {
133
+ // Specific variant is non-exhaustive
134
+ #[non_exhaustive]
135
+ Error { code: u32, message: String },
136
+
137
+ Ok(Data),
138
+ }
139
+
140
+ // Can destructure Ok normally
141
+ // But Error requires `..` to handle future fields
142
+ match msg {
143
+ Message::Ok(data) => {},
144
+ Message::Error { code, message, .. } => {}, // `..` required
145
+ }
146
+ ```
147
+
148
+ ## When to Use
149
+
150
+ ```rust
151
+ // ✅ Use for public API types that may evolve
152
+ #[non_exhaustive]
153
+ pub enum ApiError { ... }
154
+
155
+ #[non_exhaustive]
156
+ pub struct Options { ... }
157
+
158
+ // ✅ Use for error types
159
+ #[non_exhaustive]
160
+ pub enum MyError { ... }
161
+
162
+ // ❌ Don't use for internal types
163
+ enum InternalState { ... } // Not public, no concern
164
+
165
+ // ❌ Don't use for stable, complete types
166
+ pub enum Ordering { // Less, Equal, Greater is complete
167
+ Less,
168
+ Equal,
169
+ Greater,
170
+ }
171
+ ```
172
+
173
+ ## See Also
174
+
175
+ - [api-sealed-trait](./api-sealed-trait.md) - Controlling trait implementations
176
+ - [err-custom-type](./err-custom-type.md) - Error type design
177
+ - [api-builder-pattern](./api-builder-pattern.md) - Alternative to struct literals
@@ -0,0 +1,184 @@
1
+ # api-parse-dont-validate
2
+
3
+ > Parse into validated types at boundaries
4
+
5
+ ## Why It Matters
6
+
7
+ Instead of validating data and hoping you remember to check everywhere, parse it into a type that can only be constructed from valid data. The type system then guarantees validity - you can't forget to validate because invalid states are unrepresentable.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ // Validation scattered throughout codebase
13
+ fn send_email(email: &str) -> Result<(), Error> {
14
+ // Did someone validate this already? Who knows!
15
+ if !is_valid_email(email) {
16
+ return Err(Error::InvalidEmail);
17
+ }
18
+ // Send email...
19
+ }
20
+
21
+ fn add_to_mailing_list(email: &str) -> Result<(), Error> {
22
+ // Duplicate validation, or did we forget?
23
+ if !is_valid_email(email) {
24
+ return Err(Error::InvalidEmail);
25
+ }
26
+ // Add to list...
27
+ }
28
+
29
+ // Easy to forget validation
30
+ fn process_user_email(email: &str) {
31
+ // Oops, no validation!
32
+ database.store_email(email);
33
+ }
34
+ ```
35
+
36
+ ## Good
37
+
38
+ ```rust
39
+ /// A validated email address.
40
+ /// Can only be constructed via `Email::parse()`.
41
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
42
+ pub struct Email(String);
43
+
44
+ impl Email {
45
+ /// Parses and validates an email address.
46
+ pub fn parse(s: impl Into<String>) -> Result<Self, EmailError> {
47
+ let s = s.into();
48
+ if Self::is_valid(&s) {
49
+ Ok(Email(s))
50
+ } else {
51
+ Err(EmailError::Invalid)
52
+ }
53
+ }
54
+
55
+ fn is_valid(s: &str) -> bool {
56
+ s.contains('@') && s.len() > 3 // Simplified
57
+ }
58
+
59
+ pub fn as_str(&self) -> &str {
60
+ &self.0
61
+ }
62
+ }
63
+
64
+ // Now functions can accept Email - guaranteed valid!
65
+ fn send_email(email: &Email) -> Result<(), Error> {
66
+ // No validation needed - Email is always valid
67
+ smtp_send(email.as_str())
68
+ }
69
+
70
+ fn add_to_mailing_list(email: Email) {
71
+ // No validation needed
72
+ list.push(email);
73
+ }
74
+ ```
75
+
76
+ ## More Examples
77
+
78
+ ```rust
79
+ // Port number (1-65535)
80
+ pub struct Port(u16);
81
+
82
+ impl Port {
83
+ pub fn new(n: u16) -> Option<Self> {
84
+ if n > 0 { Some(Port(n)) } else { None }
85
+ }
86
+
87
+ pub fn get(&self) -> u16 {
88
+ self.0
89
+ }
90
+ }
91
+
92
+ // Non-empty string
93
+ pub struct NonEmptyString(String);
94
+
95
+ impl NonEmptyString {
96
+ pub fn new(s: impl Into<String>) -> Option<Self> {
97
+ let s = s.into();
98
+ if s.is_empty() { None } else { Some(Self(s)) }
99
+ }
100
+ }
101
+
102
+ // Positive integer
103
+ pub struct PositiveI32(i32);
104
+
105
+ impl PositiveI32 {
106
+ pub fn new(n: i32) -> Option<Self> {
107
+ if n > 0 { Some(Self(n)) } else { None }
108
+ }
109
+ }
110
+
111
+ // Bounded value
112
+ pub struct Percentage(u8);
113
+
114
+ impl Percentage {
115
+ pub fn new(n: u8) -> Option<Self> {
116
+ if n <= 100 { Some(Self(n)) } else { None }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ## Parsing at Boundaries
122
+
123
+ ```rust
124
+ // Parse at the system boundary (API, CLI, config file)
125
+ fn handle_request(raw: RawRequest) -> Result<Response, Error> {
126
+ // Parse ALL inputs upfront
127
+ let email = Email::parse(&raw.email)?;
128
+ let age = Age::parse(raw.age)?;
129
+ let username = Username::parse(&raw.username)?;
130
+
131
+ // Now work with validated types
132
+ process_user(email, age, username)
133
+ }
134
+
135
+ fn process_user(email: Email, age: Age, username: Username) {
136
+ // All inputs guaranteed valid - no checks needed
137
+ }
138
+ ```
139
+
140
+ ## Evidence from sqlx
141
+
142
+ ```rust
143
+ // sqlx parses SQL at compile time, ensuring query validity
144
+ // https://github.com/launchbadge/sqlx/blob/master/src/macros/mod.rs
145
+
146
+ // The query! macro parses and validates SQL
147
+ let user = sqlx::query!("SELECT * FROM users WHERE id = ?", id)
148
+ .fetch_one(&pool)
149
+ .await?;
150
+
151
+ // If SQL is invalid, compilation fails - invalid state unrepresentable
152
+ ```
153
+
154
+ ## Combining with Display
155
+
156
+ ```rust
157
+ use std::fmt;
158
+
159
+ pub struct Email(String);
160
+
161
+ impl Email {
162
+ pub fn parse(s: &str) -> Result<Self, EmailError> { ... }
163
+ }
164
+
165
+ // Implement Display for easy printing
166
+ impl fmt::Display for Email {
167
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168
+ write!(f, "{}", self.0)
169
+ }
170
+ }
171
+
172
+ // Implement AsRef for easy borrowing
173
+ impl AsRef<str> for Email {
174
+ fn as_ref(&self) -> &str {
175
+ &self.0
176
+ }
177
+ }
178
+ ```
179
+
180
+ ## See Also
181
+
182
+ - [api-newtype-safety](api-newtype-safety.md) - Use newtypes for type safety
183
+ - [type-newtype-validated](type-newtype-validated.md) - Newtypes for validated data
184
+ - [api-typestate](api-typestate.md) - Compile-time state machines