agy-superpowers 5.1.4 → 5.1.6
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/package.json +1 -1
- package/template/agent/rules/debug-confirmation-policy.md +34 -0
- package/template/agent/rules/language-matching.md +32 -0
- package/template/agent/skills/rust-developer/SKILL.md +281 -0
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
|
@@ -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
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# api-serde-optional
|
|
2
|
+
|
|
3
|
+
> Make serde a feature flag, not a hard dependency for library crates
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Not all users of your library need serialization. Making serde a required dependency adds compile time and binary size for everyone. Feature flags let users opt-in to serde support only when needed, following Rust's philosophy of zero-cost abstractions and minimal dependencies.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Cargo.toml
|
|
13
|
+
[dependencies]
|
|
14
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
15
|
+
|
|
16
|
+
// lib.rs
|
|
17
|
+
use serde::{Serialize, Deserialize};
|
|
18
|
+
|
|
19
|
+
// Every user pays for serde, even if they don't need it
|
|
20
|
+
#[derive(Serialize, Deserialize)]
|
|
21
|
+
pub struct Config {
|
|
22
|
+
pub name: String,
|
|
23
|
+
pub value: i32,
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Good
|
|
28
|
+
|
|
29
|
+
```rust
|
|
30
|
+
// Cargo.toml
|
|
31
|
+
[dependencies]
|
|
32
|
+
serde = { version = "1.0", features = ["derive"], optional = true }
|
|
33
|
+
|
|
34
|
+
[features]
|
|
35
|
+
default = []
|
|
36
|
+
serde = ["dep:serde"]
|
|
37
|
+
|
|
38
|
+
// lib.rs
|
|
39
|
+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
40
|
+
pub struct Config {
|
|
41
|
+
pub name: String,
|
|
42
|
+
pub value: i32,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Users opt-in:
|
|
46
|
+
// my_crate = { version = "1.0", features = ["serde"] }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Macro Pattern
|
|
50
|
+
|
|
51
|
+
```rust
|
|
52
|
+
// Reusable macro for serde derives
|
|
53
|
+
#[cfg(feature = "serde")]
|
|
54
|
+
macro_rules! impl_serde {
|
|
55
|
+
($($t:ty),*) => {
|
|
56
|
+
$(
|
|
57
|
+
impl serde::Serialize for $t {
|
|
58
|
+
// ...
|
|
59
|
+
}
|
|
60
|
+
impl<'de> serde::Deserialize<'de> for $t {
|
|
61
|
+
// ...
|
|
62
|
+
}
|
|
63
|
+
)*
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#[cfg(not(feature = "serde"))]
|
|
68
|
+
macro_rules! impl_serde {
|
|
69
|
+
($($t:ty),*) => {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Or use cfg_attr for derived impls
|
|
73
|
+
#[derive(Debug, Clone)]
|
|
74
|
+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
75
|
+
pub struct Point {
|
|
76
|
+
pub x: f64,
|
|
77
|
+
pub y: f64,
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Feature Documentation
|
|
82
|
+
|
|
83
|
+
```rust
|
|
84
|
+
// lib.rs
|
|
85
|
+
|
|
86
|
+
//! # Features
|
|
87
|
+
//!
|
|
88
|
+
//! - `serde`: Enables `Serialize` and `Deserialize` implementations for all types.
|
|
89
|
+
//!
|
|
90
|
+
//! # Example with serde
|
|
91
|
+
//!
|
|
92
|
+
//! ```toml
|
|
93
|
+
//! [dependencies]
|
|
94
|
+
//! my_crate = { version = "1.0", features = ["serde"] }
|
|
95
|
+
//! ```
|
|
96
|
+
|
|
97
|
+
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
98
|
+
|
|
99
|
+
/// A configuration type.
|
|
100
|
+
///
|
|
101
|
+
/// When the `serde` feature is enabled, this type implements
|
|
102
|
+
/// `Serialize` and `Deserialize`.
|
|
103
|
+
#[derive(Debug, Clone)]
|
|
104
|
+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
105
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
|
106
|
+
pub struct Config {
|
|
107
|
+
pub name: String,
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Multiple Optional Dependencies
|
|
112
|
+
|
|
113
|
+
```rust
|
|
114
|
+
// Cargo.toml
|
|
115
|
+
[dependencies]
|
|
116
|
+
serde = { version = "1.0", features = ["derive"], optional = true }
|
|
117
|
+
rkyv = { version = "0.7", optional = true }
|
|
118
|
+
borsh = { version = "0.10", optional = true }
|
|
119
|
+
|
|
120
|
+
[features]
|
|
121
|
+
default = []
|
|
122
|
+
serde = ["dep:serde"]
|
|
123
|
+
rkyv = ["dep:rkyv"]
|
|
124
|
+
borsh = ["dep:borsh"]
|
|
125
|
+
|
|
126
|
+
// lib.rs
|
|
127
|
+
#[derive(Debug, Clone)]
|
|
128
|
+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
129
|
+
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
|
|
130
|
+
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
|
131
|
+
pub struct Message {
|
|
132
|
+
pub id: u64,
|
|
133
|
+
pub content: String,
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Testing with Features
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Test without serde
|
|
141
|
+
cargo test
|
|
142
|
+
|
|
143
|
+
# Test with serde
|
|
144
|
+
cargo test --features serde
|
|
145
|
+
|
|
146
|
+
# Test all feature combinations
|
|
147
|
+
cargo test --all-features
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```rust
|
|
151
|
+
// Test serde round-trip when feature enabled
|
|
152
|
+
#[cfg(feature = "serde")]
|
|
153
|
+
#[test]
|
|
154
|
+
fn test_serde_roundtrip() {
|
|
155
|
+
let config = Config { name: "test".into() };
|
|
156
|
+
let json = serde_json::to_string(&config).unwrap();
|
|
157
|
+
let parsed: Config = serde_json::from_str(&json).unwrap();
|
|
158
|
+
assert_eq!(config, parsed);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## When to Make Serde Required
|
|
163
|
+
|
|
164
|
+
```rust
|
|
165
|
+
// ✅ Required: Library is about serialization
|
|
166
|
+
// (e.g., json-schema, config-file parser)
|
|
167
|
+
[dependencies]
|
|
168
|
+
serde = "1.0"
|
|
169
|
+
|
|
170
|
+
// ✅ Required: Domain heavily uses serde
|
|
171
|
+
// (e.g., API client, data format library)
|
|
172
|
+
|
|
173
|
+
// ❌ Optional: General-purpose utility library
|
|
174
|
+
// ❌ Optional: Math/algorithm library
|
|
175
|
+
// ❌ Optional: Most libraries!
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## See Also
|
|
179
|
+
|
|
180
|
+
- [proj-lib-main-split](./proj-lib-main-split.md) - Library structure
|
|
181
|
+
- [api-common-traits](./api-common-traits.md) - Core trait implementations
|
|
182
|
+
- [lint-deny-correctness](./lint-deny-correctness.md) - Feature testing
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# api-typestate
|
|
2
|
+
|
|
3
|
+
> Use typestate pattern to encode state machine invariants in the type system
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
State machines with runtime state checks ("are we connected?", "is the transaction started?") can have invalid transitions. The typestate pattern uses different types for each state, making invalid state transitions compile errors. The compiler enforces your state machine.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
struct Connection {
|
|
13
|
+
state: ConnectionState,
|
|
14
|
+
socket: Option<TcpStream>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
enum ConnectionState {
|
|
18
|
+
Disconnected,
|
|
19
|
+
Connected,
|
|
20
|
+
Authenticated,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl Connection {
|
|
24
|
+
fn send(&mut self, data: &[u8]) -> Result<(), Error> {
|
|
25
|
+
// Runtime check - can fail if called in wrong state
|
|
26
|
+
if self.state != ConnectionState::Authenticated {
|
|
27
|
+
return Err(Error::NotAuthenticated);
|
|
28
|
+
}
|
|
29
|
+
self.socket.as_mut().unwrap().write_all(data)?;
|
|
30
|
+
Ok(())
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn authenticate(&mut self, password: &str) -> Result<(), Error> {
|
|
34
|
+
// Runtime check - can fail
|
|
35
|
+
if self.state != ConnectionState::Connected {
|
|
36
|
+
return Err(Error::NotConnected);
|
|
37
|
+
}
|
|
38
|
+
// ...
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Bug: forgot to authenticate
|
|
43
|
+
let mut conn = Connection::new();
|
|
44
|
+
conn.connect()?;
|
|
45
|
+
conn.send(b"data")?; // Runtime error: NotAuthenticated
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Good
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
// Different types for each state
|
|
52
|
+
struct Disconnected;
|
|
53
|
+
struct Connected { socket: TcpStream }
|
|
54
|
+
struct Authenticated { socket: TcpStream, session: Session }
|
|
55
|
+
|
|
56
|
+
struct Connection<State> {
|
|
57
|
+
state: State,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl Connection<Disconnected> {
|
|
61
|
+
fn new() -> Self {
|
|
62
|
+
Connection { state: Disconnected }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn connect(self, addr: &str) -> Result<Connection<Connected>, Error> {
|
|
66
|
+
let socket = TcpStream::connect(addr)?;
|
|
67
|
+
Ok(Connection { state: Connected { socket } })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
impl Connection<Connected> {
|
|
72
|
+
fn authenticate(self, password: &str) -> Result<Connection<Authenticated>, Error> {
|
|
73
|
+
let session = do_auth(&self.state.socket, password)?;
|
|
74
|
+
Ok(Connection {
|
|
75
|
+
state: Authenticated { socket: self.state.socket, session }
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl Connection<Authenticated> {
|
|
81
|
+
fn send(&mut self, data: &[u8]) -> Result<(), Error> {
|
|
82
|
+
// No runtime check needed - type guarantees we're authenticated
|
|
83
|
+
self.state.socket.write_all(data)?;
|
|
84
|
+
Ok(())
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Bug: forgot to authenticate
|
|
89
|
+
let conn = Connection::new();
|
|
90
|
+
let conn = conn.connect("server:8080")?;
|
|
91
|
+
conn.send(b"data"); // Compile error! send() not available on Connection<Connected>
|
|
92
|
+
|
|
93
|
+
// Correct usage
|
|
94
|
+
let conn = Connection::new();
|
|
95
|
+
let conn = conn.connect("server:8080")?;
|
|
96
|
+
let mut conn = conn.authenticate("secret")?;
|
|
97
|
+
conn.send(b"data")?; // Works - type is Connection<Authenticated>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Builder Typestate
|
|
101
|
+
|
|
102
|
+
```rust
|
|
103
|
+
// Enforce required fields via typestate
|
|
104
|
+
struct BuilderNoUrl;
|
|
105
|
+
struct BuilderWithUrl { url: String }
|
|
106
|
+
|
|
107
|
+
struct RequestBuilder<State> {
|
|
108
|
+
state: State,
|
|
109
|
+
timeout: Option<Duration>,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
impl RequestBuilder<BuilderNoUrl> {
|
|
113
|
+
fn new() -> Self {
|
|
114
|
+
RequestBuilder {
|
|
115
|
+
state: BuilderNoUrl,
|
|
116
|
+
timeout: None,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fn url(self, url: &str) -> RequestBuilder<BuilderWithUrl> {
|
|
121
|
+
RequestBuilder {
|
|
122
|
+
state: BuilderWithUrl { url: url.to_string() },
|
|
123
|
+
timeout: self.timeout,
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
impl RequestBuilder<BuilderWithUrl> {
|
|
129
|
+
fn timeout(mut self, t: Duration) -> Self {
|
|
130
|
+
self.timeout = Some(t);
|
|
131
|
+
self
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Only available once URL is set
|
|
135
|
+
fn build(self) -> Request {
|
|
136
|
+
Request {
|
|
137
|
+
url: self.state.url,
|
|
138
|
+
timeout: self.timeout,
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Compile error: build() not available
|
|
144
|
+
let bad = RequestBuilder::new().build();
|
|
145
|
+
|
|
146
|
+
// Correct: must set URL first
|
|
147
|
+
let good = RequestBuilder::new()
|
|
148
|
+
.url("https://example.com")
|
|
149
|
+
.timeout(Duration::from_secs(30))
|
|
150
|
+
.build();
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Transaction Example
|
|
154
|
+
|
|
155
|
+
```rust
|
|
156
|
+
struct NotStarted;
|
|
157
|
+
struct InProgress { tx_id: u64 }
|
|
158
|
+
struct Committed;
|
|
159
|
+
|
|
160
|
+
struct Transaction<State> {
|
|
161
|
+
conn: Connection,
|
|
162
|
+
state: State,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
impl Transaction<NotStarted> {
|
|
166
|
+
fn begin(conn: Connection) -> Result<Transaction<InProgress>, Error> {
|
|
167
|
+
let tx_id = conn.execute("BEGIN")?;
|
|
168
|
+
Ok(Transaction {
|
|
169
|
+
conn,
|
|
170
|
+
state: InProgress { tx_id },
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
impl Transaction<InProgress> {
|
|
176
|
+
fn execute(&mut self, sql: &str) -> Result<(), Error> {
|
|
177
|
+
self.conn.execute(sql)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fn commit(self) -> Result<Transaction<Committed>, Error> {
|
|
181
|
+
self.conn.execute("COMMIT")?;
|
|
182
|
+
Ok(Transaction {
|
|
183
|
+
conn: self.conn,
|
|
184
|
+
state: Committed,
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fn rollback(self) -> Connection {
|
|
189
|
+
let _ = self.conn.execute("ROLLBACK");
|
|
190
|
+
self.conn
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## See Also
|
|
196
|
+
|
|
197
|
+
- [api-builder-pattern](./api-builder-pattern.md) - Basic builder pattern
|
|
198
|
+
- [api-parse-dont-validate](./api-parse-dont-validate.md) - Type-driven invariants
|
|
199
|
+
- [api-sealed-trait](./api-sealed-trait.md) - Restricting trait implementations
|