agy-superpowers 5.1.4 → 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 (182) hide show
  1. package/package.json +1 -1
  2. package/template/agent/skills/rust-developer/SKILL.md +281 -0
  3. package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
  4. package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
  5. package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
  6. package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
  7. package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
  8. package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
  9. package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
  10. package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
  11. package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
  12. package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
  13. package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
  14. package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
  15. package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
  16. package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
  17. package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
  18. package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
  19. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
  20. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
  21. package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
  22. package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
  23. package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
  24. package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
  25. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
  26. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
  27. package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
  28. package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
  29. package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
  30. package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
  31. package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
  32. package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
  33. package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
  34. package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
  35. package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
  36. package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
  37. package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
  38. package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
  39. package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
  40. package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
  41. package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
  42. package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
  43. package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
  44. package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
  45. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
  46. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
  47. package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
  48. package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
  49. package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
  50. package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
  51. package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
  52. package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
  53. package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
  54. package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
  55. package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
  56. package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
  57. package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
  58. package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
  59. package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
  60. package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
  61. package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
  62. package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
  63. package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
  64. package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
  65. package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
  66. package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
  67. package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
  68. package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
  69. package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
  70. package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
  71. package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
  72. package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
  73. package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
  74. package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
  75. package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
  76. package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
  77. package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
  78. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
  79. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
  80. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
  81. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
  82. package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
  83. package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
  84. package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
  85. package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
  86. package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
  87. package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
  88. package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
  89. package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
  90. package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
  91. package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
  92. package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
  93. package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
  94. package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
  95. package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
  96. package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
  97. package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
  98. package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
  99. package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
  100. package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
  101. package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
  102. package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
  103. package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
  104. package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
  105. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
  106. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
  107. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
  108. package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
  109. package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
  110. package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
  111. package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
  112. package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
  113. package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
  114. package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
  115. package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
  116. package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
  117. package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
  118. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
  119. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
  120. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
  121. package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
  122. package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
  123. package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
  124. package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
  125. package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
  126. package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
  127. package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
  128. package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
  129. package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
  130. package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
  131. package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
  132. package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
  133. package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
  134. package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
  135. package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
  136. package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
  137. package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
  138. package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
  139. package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
  140. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
  141. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
  142. package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
  143. package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
  144. package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
  145. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
  146. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
  147. package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
  148. package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
  149. package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
  150. package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
  151. package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
  152. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
  153. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
  154. package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
  155. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
  156. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
  157. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
  158. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
  159. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
  160. package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
  161. package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
  162. package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
  163. package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
  164. package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
  165. package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
  166. package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
  167. package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
  168. package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
  169. package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
  170. package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
  171. package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
  172. package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
  173. package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
  174. package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
  175. package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
  176. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
  177. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
  178. package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
  179. package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
  180. package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
  181. package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
  182. package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
@@ -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
@@ -0,0 +1,168 @@
1
+ # api-sealed-trait
2
+
3
+ > Use sealed traits to prevent external implementations while allowing use
4
+
5
+ ## Why It Matters
6
+
7
+ Public traits can be implemented by anyone, which may be undesirable when you need to guarantee behavior or add methods in future versions. A sealed trait can be used by external code but not implemented by it, giving you control over implementations while maintaining a usable API.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ // Anyone can implement this trait
13
+ pub trait DatabaseDriver {
14
+ fn connect(&self, url: &str) -> Connection;
15
+ fn execute(&self, query: &str) -> Result<Rows, Error>;
16
+ }
17
+
18
+ // External crate implements it incorrectly
19
+ impl DatabaseDriver for MyBadDriver {
20
+ fn connect(&self, url: &str) -> Connection {
21
+ // Buggy implementation that doesn't handle errors
22
+ unsafe { force_connect(url) }
23
+ }
24
+ }
25
+
26
+ // Later, you want to add a required method - BREAKING CHANGE
27
+ pub trait DatabaseDriver {
28
+ fn connect(&self, url: &str) -> Connection;
29
+ fn execute(&self, query: &str) -> Result<Rows, Error>;
30
+ fn transaction(&self) -> Transaction; // External impls now broken!
31
+ }
32
+ ```
33
+
34
+ ## Good
35
+
36
+ ```rust
37
+ // Create a private module with a private trait
38
+ mod private {
39
+ pub trait Sealed {}
40
+ }
41
+
42
+ // Public trait requires the private trait
43
+ pub trait DatabaseDriver: private::Sealed {
44
+ fn connect(&self, url: &str) -> Connection;
45
+ fn execute(&self, query: &str) -> Result<Rows, Error>;
46
+ }
47
+
48
+ // Only your crate can implement Sealed, thus DatabaseDriver
49
+ pub struct PostgresDriver;
50
+ impl private::Sealed for PostgresDriver {}
51
+ impl DatabaseDriver for PostgresDriver {
52
+ fn connect(&self, url: &str) -> Connection { ... }
53
+ fn execute(&self, query: &str) -> Result<Rows, Error> { ... }
54
+ }
55
+
56
+ pub struct MySqlDriver;
57
+ impl private::Sealed for MySqlDriver {}
58
+ impl DatabaseDriver for MySqlDriver {
59
+ fn connect(&self, url: &str) -> Connection { ... }
60
+ fn execute(&self, query: &str) -> Result<Rows, Error> { ... }
61
+ }
62
+
63
+ // External crate cannot implement - private::Sealed is not accessible
64
+ // impl DatabaseDriver for ExternalDriver { } // Error!
65
+
66
+ // But external code CAN use the trait
67
+ fn use_driver(driver: &impl DatabaseDriver) {
68
+ let conn = driver.connect("postgres://localhost");
69
+ }
70
+ ```
71
+
72
+ ## Full Pattern
73
+
74
+ ```rust
75
+ pub mod db {
76
+ mod private {
77
+ pub trait Sealed {}
78
+ }
79
+
80
+ /// Database driver trait.
81
+ ///
82
+ /// This trait is sealed and cannot be implemented outside this crate.
83
+ pub trait Driver: private::Sealed {
84
+ /// Connects to the database.
85
+ fn connect(&self, url: &str) -> Result<Connection, Error>;
86
+
87
+ /// Executes a query.
88
+ fn execute(&self, sql: &str) -> Result<Rows, Error>;
89
+ }
90
+
91
+ pub struct Postgres;
92
+ impl private::Sealed for Postgres {}
93
+ impl Driver for Postgres { ... }
94
+
95
+ pub struct Sqlite;
96
+ impl private::Sealed for Sqlite {}
97
+ impl Driver for Sqlite { ... }
98
+ }
99
+
100
+ // Usage works fine
101
+ use db::{Driver, Postgres};
102
+
103
+ fn query(driver: &impl Driver) {
104
+ driver.execute("SELECT 1")?;
105
+ }
106
+
107
+ query(&Postgres);
108
+ ```
109
+
110
+ ## Benefits of Sealing
111
+
112
+ ```rust
113
+ // 1. Add methods without breaking changes
114
+ pub trait Format: private::Sealed {
115
+ fn format(&self) -> String;
116
+
117
+ // Added later - not breaking because no external impls exist
118
+ fn format_pretty(&self) -> String {
119
+ self.format() // Default implementation
120
+ }
121
+ }
122
+
123
+ // 2. Guarantee invariants
124
+ pub trait SafeBuffer: private::Sealed {
125
+ // You control all implementations, so you know they're all correct
126
+ fn get(&self, index: usize) -> Option<&u8>;
127
+ }
128
+
129
+ // 3. Use as marker traits
130
+ pub trait ValidConfig: private::Sealed {}
131
+ // Only validated configs implement this
132
+ ```
133
+
134
+ ## Partially Sealed
135
+
136
+ ```rust
137
+ // Allow implementing some methods but not all
138
+ mod private {
139
+ pub trait SealedCore {}
140
+ }
141
+
142
+ pub trait Plugin: private::SealedCore {
143
+ // Sealed - only we implement
144
+ fn initialize(&self);
145
+ fn shutdown(&self);
146
+
147
+ // Open - users can override
148
+ fn name(&self) -> &str { "unnamed" }
149
+ }
150
+
151
+ // Only we can add new required sealed methods
152
+ // Users can customize open methods
153
+ ```
154
+
155
+ ## When to Seal
156
+
157
+ | Seal When | Don't Seal When |
158
+ |-----------|-----------------|
159
+ | API stability is critical | You want extension points |
160
+ | Implementation correctness is hard | Users need custom implementations |
161
+ | You'll add methods later | Trait is simple and stable |
162
+ | Safety invariants required | Standard patterns (Iterator, etc.) |
163
+
164
+ ## See Also
165
+
166
+ - [api-non-exhaustive](./api-non-exhaustive.md) - Related pattern for enums/structs
167
+ - [api-extension-trait](./api-extension-trait.md) - Adding methods to external types
168
+ - [api-typestate](./api-typestate.md) - Compile-time state guarantees