agy-superpowers 5.2.2 → 5.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -150
- package/package.json +1 -1
- package/template/agent/rules/CLAUDE.md +80 -0
- package/template/agent/rules/code-styles.md +31 -32
- package/template/agent/rules/debug-confirmation-policy.md +2 -0
- package/template/agent/rules/file-length-policy.md +2 -0
- package/template/agent/rules/git-policy.md +7 -0
- package/template/agent/rules/language-matching.md +2 -0
- package/template/agent/rules/scratch-scripts.md +39 -0
- package/template/agent/rules/superpowers.md +8 -51
- package/template/agent/skills/executing-plans/SKILL.md +17 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +16 -0
- package/template/agent/skills/test-driven-development/SKILL.md +16 -0
- package/template/agent/skills/verification-before-completion/SKILL.md +22 -0
- package/template/agent/skills/writing-plans/SKILL.md +16 -0
- package/template/agent/skills/ai-integrated-product/SKILL.md +0 -57
- package/template/agent/skills/analytics-setup/SKILL.md +0 -51
- package/template/agent/skills/api-design/SKILL.md +0 -193
- package/template/agent/skills/app-store-optimizer/SKILL.md +0 -127
- package/template/agent/skills/auth-and-identity/SKILL.md +0 -167
- package/template/agent/skills/backend-developer/SKILL.md +0 -148
- package/template/agent/skills/bootstrapper-finance/SKILL.md +0 -55
- package/template/agent/skills/chrome-extension-developer/SKILL.md +0 -53
- package/template/agent/skills/community-manager/SKILL.md +0 -115
- package/template/agent/skills/content-marketer/SKILL.md +0 -111
- package/template/agent/skills/conversion-optimizer/SKILL.md +0 -142
- package/template/agent/skills/cto-architect/SKILL.md +0 -133
- package/template/agent/skills/customer-success-manager/SKILL.md +0 -126
- package/template/agent/skills/data-analyst/SKILL.md +0 -147
- package/template/agent/skills/devops-engineer/SKILL.md +0 -117
- package/template/agent/skills/email-infrastructure/SKILL.md +0 -164
- package/template/agent/skills/game-design/SKILL.md +0 -194
- package/template/agent/skills/game-developer/SKILL.md +0 -175
- package/template/agent/skills/growth-hacker/SKILL.md +0 -122
- package/template/agent/skills/idea-validator/SKILL.md +0 -55
- package/template/agent/skills/indie-legal/SKILL.md +0 -53
- package/template/agent/skills/influencer-marketer/SKILL.md +0 -141
- package/template/agent/skills/landing-page-builder/SKILL.md +0 -59
- package/template/agent/skills/launch-strategist/SKILL.md +0 -62
- package/template/agent/skills/market-researcher/SKILL.md +0 -53
- package/template/agent/skills/micro-saas-builder/SKILL.md +0 -56
- package/template/agent/skills/monetization-strategist/SKILL.md +0 -119
- package/template/agent/skills/paid-acquisition-specialist/SKILL.md +0 -119
- package/template/agent/skills/pricing-psychologist/SKILL.md +0 -58
- package/template/agent/skills/real-time-features/SKILL.md +0 -194
- package/template/agent/skills/retention-specialist/SKILL.md +0 -123
- package/template/agent/skills/rust-developer/SKILL.md +0 -281
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +0 -231
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +0 -132
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +0 -121
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +0 -165
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +0 -163
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +0 -184
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +0 -182
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +0 -199
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +0 -185
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +0 -203
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +0 -195
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +0 -191
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +0 -198
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +0 -116
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +0 -128
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +0 -179
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +0 -145
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +0 -115
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +0 -107
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +0 -157
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +0 -174
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +0 -164
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +0 -99
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +0 -104
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +0 -94
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +0 -78
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +0 -76
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +0 -129
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +0 -86
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +0 -92
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +0 -101
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +0 -181
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +0 -105
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +0 -97
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +0 -119
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +0 -153
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +0 -150
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +0 -148
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +0 -186
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +0 -226
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +0 -188
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +0 -131
- package/template/agent/skills/saas-architect/SKILL.md +0 -139
- package/template/agent/skills/security-engineer/SKILL.md +0 -133
- package/template/agent/skills/seo-specialist/SKILL.md +0 -130
- package/template/agent/skills/solo-founder-ops/SKILL.md +0 -56
|
@@ -1,162 +0,0 @@
|
|
|
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
|
|
@@ -1,177 +0,0 @@
|
|
|
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
|
package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
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
|
|
@@ -1,168 +0,0 @@
|
|
|
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
|