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,167 +0,0 @@
|
|
|
1
|
-
# anti-stringly-typed
|
|
2
|
-
|
|
3
|
-
> Don't use strings where enums or newtypes would provide type safety
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Strings are the most primitive way to represent data—they accept any value, provide no validation, and offer no IDE support. When you have a fixed set of valid values or a semantic type, use enums or newtypes. The compiler catches mistakes at compile time instead of runtime.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
fn process_order(status: &str, priority: &str) {
|
|
13
|
-
// What are valid statuses? "pending"? "Pending"? "PENDING"?
|
|
14
|
-
// What are valid priorities? "high"? "1"? "urgent"?
|
|
15
|
-
match status {
|
|
16
|
-
"pending" => { ... }
|
|
17
|
-
"completed" => { ... }
|
|
18
|
-
_ => panic!("unknown status"), // Runtime error
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
struct User {
|
|
23
|
-
email: String, // Any string, even "not an email"
|
|
24
|
-
phone: String, // Any string, even "hello"
|
|
25
|
-
user_id: String, // Could be confused with other string IDs
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Easy to make mistakes
|
|
29
|
-
process_order("complete", "high"); // Typo: "complete" vs "completed"
|
|
30
|
-
process_order("high", "pending"); // Swapped arguments - compiles!
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Good
|
|
34
|
-
|
|
35
|
-
```rust
|
|
36
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
37
|
-
enum OrderStatus {
|
|
38
|
-
Pending,
|
|
39
|
-
Processing,
|
|
40
|
-
Completed,
|
|
41
|
-
Cancelled,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
45
|
-
enum Priority {
|
|
46
|
-
Low,
|
|
47
|
-
Medium,
|
|
48
|
-
High,
|
|
49
|
-
Critical,
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
fn process_order(status: OrderStatus, priority: Priority) {
|
|
53
|
-
match status {
|
|
54
|
-
OrderStatus::Pending => { ... }
|
|
55
|
-
OrderStatus::Processing => { ... }
|
|
56
|
-
OrderStatus::Completed => { ... }
|
|
57
|
-
OrderStatus::Cancelled => { ... }
|
|
58
|
-
} // Exhaustive - compiler checks all cases
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Validated newtypes
|
|
62
|
-
struct Email(String);
|
|
63
|
-
struct PhoneNumber(String);
|
|
64
|
-
struct UserId(u64);
|
|
65
|
-
|
|
66
|
-
impl Email {
|
|
67
|
-
pub fn new(s: &str) -> Result<Self, ValidationError> {
|
|
68
|
-
if is_valid_email(s) {
|
|
69
|
-
Ok(Email(s.to_string()))
|
|
70
|
-
} else {
|
|
71
|
-
Err(ValidationError::InvalidEmail)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
struct User {
|
|
77
|
-
email: Email, // Must be valid email
|
|
78
|
-
phone: PhoneNumber, // Must be valid phone
|
|
79
|
-
user_id: UserId, // Can't confuse with other IDs
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Compile errors catch mistakes
|
|
83
|
-
process_order(OrderStatus::Completed, Priority::High); // Clear and correct
|
|
84
|
-
process_order(Priority::High, OrderStatus::Pending); // Compile error!
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Parsing Strings to Types
|
|
88
|
-
|
|
89
|
-
```rust
|
|
90
|
-
use std::str::FromStr;
|
|
91
|
-
|
|
92
|
-
#[derive(Debug, Clone, Copy)]
|
|
93
|
-
enum OrderStatus {
|
|
94
|
-
Pending,
|
|
95
|
-
Processing,
|
|
96
|
-
Completed,
|
|
97
|
-
Cancelled,
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
impl FromStr for OrderStatus {
|
|
101
|
-
type Err = ParseError;
|
|
102
|
-
|
|
103
|
-
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
104
|
-
match s.to_lowercase().as_str() {
|
|
105
|
-
"pending" => Ok(OrderStatus::Pending),
|
|
106
|
-
"processing" => Ok(OrderStatus::Processing),
|
|
107
|
-
"completed" => Ok(OrderStatus::Completed),
|
|
108
|
-
"cancelled" | "canceled" => Ok(OrderStatus::Cancelled),
|
|
109
|
-
_ => Err(ParseError::UnknownStatus(s.to_string())),
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Parse at boundary, use types internally
|
|
115
|
-
fn handle_request(status_str: &str) -> Result<(), Error> {
|
|
116
|
-
let status: OrderStatus = status_str.parse()?; // Validate once
|
|
117
|
-
process_order(status); // Type-safe from here
|
|
118
|
-
Ok(())
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## With Serde
|
|
123
|
-
|
|
124
|
-
```rust
|
|
125
|
-
use serde::{Serialize, Deserialize};
|
|
126
|
-
|
|
127
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
128
|
-
#[serde(rename_all = "snake_case")]
|
|
129
|
-
enum Status {
|
|
130
|
-
Pending,
|
|
131
|
-
InProgress,
|
|
132
|
-
Completed,
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// JSON: {"status": "in_progress"}
|
|
136
|
-
// Deserialization validates automatically
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Error Messages
|
|
140
|
-
|
|
141
|
-
```rust
|
|
142
|
-
#[derive(Debug, Clone, Copy)]
|
|
143
|
-
enum Color {
|
|
144
|
-
Red,
|
|
145
|
-
Green,
|
|
146
|
-
Blue,
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
impl std::fmt::Display for Color {
|
|
150
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
151
|
-
match self {
|
|
152
|
-
Color::Red => write!(f, "red"),
|
|
153
|
-
Color::Green => write!(f, "green"),
|
|
154
|
-
Color::Blue => write!(f, "blue"),
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Type-safe and displayable
|
|
160
|
-
println!("Selected color: {}", Color::Red);
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## See Also
|
|
164
|
-
|
|
165
|
-
- [api-newtype-safety](./api-newtype-safety.md) - Newtype pattern
|
|
166
|
-
- [api-parse-dont-validate](./api-parse-dont-validate.md) - Parse at boundaries
|
|
167
|
-
- [type-newtype-ids](./type-newtype-ids.md) - Type-safe IDs
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# anti-type-erasure
|
|
2
|
-
|
|
3
|
-
> Don't use Box<dyn Trait> when impl Trait works
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`Box<dyn Trait>` (type erasure) introduces heap allocation and dynamic dispatch overhead. When you have a single concrete type or can use generics, `impl Trait` provides the same flexibility with zero overhead through monomorphization.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Unnecessary type erasure
|
|
13
|
-
fn get_iterator() -> Box<dyn Iterator<Item = i32>> {
|
|
14
|
-
Box::new((0..10).map(|x| x * 2))
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Boxing for no reason
|
|
18
|
-
fn make_handler() -> Box<dyn Fn(i32) -> i32> {
|
|
19
|
-
Box::new(|x| x + 1)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Vec of boxed trait objects when one type would do
|
|
23
|
-
fn get_validators() -> Vec<Box<dyn Validator>> {
|
|
24
|
-
vec![
|
|
25
|
-
Box::new(LengthValidator),
|
|
26
|
-
Box::new(RegexValidator),
|
|
27
|
-
]
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
// impl Trait - zero overhead, inlined
|
|
35
|
-
fn get_iterator() -> impl Iterator<Item = i32> {
|
|
36
|
-
(0..10).map(|x| x * 2)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// impl Fn - no boxing
|
|
40
|
-
fn make_handler() -> impl Fn(i32) -> i32 {
|
|
41
|
-
|x| x + 1
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// When mixed types are genuinely needed, Box is OK
|
|
45
|
-
fn get_validators() -> Vec<Box<dyn Validator>> {
|
|
46
|
-
// Actually different types at runtime - Box is appropriate
|
|
47
|
-
config.validators.iter()
|
|
48
|
-
.map(|v| v.create_validator())
|
|
49
|
-
.collect()
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## When to Use Box<dyn Trait>
|
|
54
|
-
|
|
55
|
-
Type erasure IS appropriate when:
|
|
56
|
-
|
|
57
|
-
```rust
|
|
58
|
-
// Heterogeneous collection of different types
|
|
59
|
-
let handlers: Vec<Box<dyn Handler>> = vec![
|
|
60
|
-
Box::new(LogHandler),
|
|
61
|
-
Box::new(MetricsHandler),
|
|
62
|
-
Box::new(AuthHandler),
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
// Type cannot be known at compile time
|
|
66
|
-
fn create_from_config(config: &Config) -> Box<dyn Database> {
|
|
67
|
-
match config.db_type {
|
|
68
|
-
DbType::Postgres => Box::new(PostgresDb::new()),
|
|
69
|
-
DbType::Sqlite => Box::new(SqliteDb::new()),
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Recursive types
|
|
74
|
-
struct Node {
|
|
75
|
-
value: i32,
|
|
76
|
-
children: Vec<Box<dyn NodeTrait>>,
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Breaking cycles in complex ownership
|
|
80
|
-
struct EventLoop {
|
|
81
|
-
handlers: Vec<Box<dyn EventHandler>>,
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Comparison
|
|
86
|
-
|
|
87
|
-
| Approach | Allocation | Dispatch | Binary Size |
|
|
88
|
-
|----------|------------|----------|-------------|
|
|
89
|
-
| `impl Trait` | Stack/inline | Static | Larger (monomorphization) |
|
|
90
|
-
| `Box<dyn Trait>` | Heap | Dynamic | Smaller |
|
|
91
|
-
| Generics `<T>` | Stack/inline | Static | Larger |
|
|
92
|
-
|
|
93
|
-
## impl Trait Positions
|
|
94
|
-
|
|
95
|
-
```rust
|
|
96
|
-
// Return position - caller doesn't need to know concrete type
|
|
97
|
-
fn process() -> impl Future<Output = Result> { }
|
|
98
|
-
|
|
99
|
-
// Argument position - like generics but simpler
|
|
100
|
-
fn handle(handler: impl Handler) { }
|
|
101
|
-
|
|
102
|
-
// Can't use in trait definitions (use associated types instead)
|
|
103
|
-
trait Processor {
|
|
104
|
-
type Output: Display; // Not impl Display
|
|
105
|
-
fn process(&self) -> Self::Output;
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Pattern: Enum Instead of dyn
|
|
110
|
-
|
|
111
|
-
```rust
|
|
112
|
-
// Instead of Box<dyn Shape>
|
|
113
|
-
enum Shape {
|
|
114
|
-
Circle { radius: f64 },
|
|
115
|
-
Rectangle { width: f64, height: f64 },
|
|
116
|
-
Triangle { base: f64, height: f64 },
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
impl Shape {
|
|
120
|
-
fn area(&self) -> f64 {
|
|
121
|
-
match self {
|
|
122
|
-
Shape::Circle { radius } => PI * radius * radius,
|
|
123
|
-
Shape::Rectangle { width, height } => width * height,
|
|
124
|
-
Shape::Triangle { base, height } => 0.5 * base * height,
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## See Also
|
|
131
|
-
|
|
132
|
-
- [anti-over-abstraction](./anti-over-abstraction.md) - Excessive generics
|
|
133
|
-
- [type-generic-bounds](./type-generic-bounds.md) - Generic constraints
|
|
134
|
-
- [mem-box-large-variant](./mem-box-large-variant.md) - Boxing enum variants
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# anti-unwrap-abuse
|
|
2
|
-
|
|
3
|
-
> Don't use `.unwrap()` in production code
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`.unwrap()` panics on `None` or `Err`, crashing your program. In production, this means lost data, failed requests, and unhappy users. It also makes debugging harder since panic messages often lack context.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Crashes if file doesn't exist
|
|
13
|
-
let content = std::fs::read_to_string("config.toml").unwrap();
|
|
14
|
-
|
|
15
|
-
// Crashes on invalid input
|
|
16
|
-
let num: i32 = user_input.parse().unwrap();
|
|
17
|
-
|
|
18
|
-
// Crashes if key missing
|
|
19
|
-
let value = map.get("key").unwrap();
|
|
20
|
-
|
|
21
|
-
// Crashes if channel closed
|
|
22
|
-
let msg = receiver.recv().unwrap();
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Good
|
|
26
|
-
|
|
27
|
-
```rust
|
|
28
|
-
// Propagate with ?
|
|
29
|
-
fn load_config() -> Result<Config, Error> {
|
|
30
|
-
let content = std::fs::read_to_string("config.toml")?;
|
|
31
|
-
Ok(toml::from_str(&content)?)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Provide default
|
|
35
|
-
let num: i32 = user_input.parse().unwrap_or(0);
|
|
36
|
-
|
|
37
|
-
// Handle missing key
|
|
38
|
-
let value = map.get("key").ok_or(Error::MissingKey)?;
|
|
39
|
-
|
|
40
|
-
// Or use if-let
|
|
41
|
-
if let Some(value) = map.get("key") {
|
|
42
|
-
process(value);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Channel with proper handling
|
|
46
|
-
match receiver.recv() {
|
|
47
|
-
Ok(msg) => handle(msg),
|
|
48
|
-
Err(_) => break, // Channel closed
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## When unwrap() Is Acceptable
|
|
53
|
-
|
|
54
|
-
```rust
|
|
55
|
-
// 1. Tests - panics are expected failures
|
|
56
|
-
#[test]
|
|
57
|
-
fn test_parse() {
|
|
58
|
-
let result = parse("valid").unwrap(); // OK in tests
|
|
59
|
-
assert_eq!(result, expected);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Const/static initialization (compile-time guaranteed)
|
|
63
|
-
static REGEX: Lazy<Regex> = Lazy::new(|| {
|
|
64
|
-
Regex::new(r"^\d+$").unwrap() // Known-valid pattern
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// 3. After a check that guarantees success
|
|
68
|
-
if map.contains_key("key") {
|
|
69
|
-
let value = map.get("key").unwrap(); // Just checked
|
|
70
|
-
}
|
|
71
|
-
// Better: use if-let or entry API instead
|
|
72
|
-
|
|
73
|
-
// 4. Truly impossible cases with proof comment
|
|
74
|
-
let last = vec.pop().unwrap();
|
|
75
|
-
// OK only if you just checked !vec.is_empty()
|
|
76
|
-
// Better: use last() or pattern match
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Alternatives to unwrap()
|
|
80
|
-
|
|
81
|
-
```rust
|
|
82
|
-
// unwrap_or - provide default
|
|
83
|
-
let x = opt.unwrap_or(default);
|
|
84
|
-
|
|
85
|
-
// unwrap_or_default - use Default trait
|
|
86
|
-
let x = opt.unwrap_or_default();
|
|
87
|
-
|
|
88
|
-
// unwrap_or_else - compute default lazily
|
|
89
|
-
let x = opt.unwrap_or_else(|| expensive_default());
|
|
90
|
-
|
|
91
|
-
// ? operator - propagate errors
|
|
92
|
-
let x = opt.ok_or(Error::Missing)?;
|
|
93
|
-
|
|
94
|
-
// if let - handle Some/Ok case
|
|
95
|
-
if let Some(x) = opt {
|
|
96
|
-
use_x(x);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// match - handle all cases
|
|
100
|
-
match opt {
|
|
101
|
-
Some(x) => use_x(x),
|
|
102
|
-
None => handle_none(),
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// map - transform if present
|
|
106
|
-
let y = opt.map(|x| x + 1);
|
|
107
|
-
|
|
108
|
-
// and_then - chain fallible operations
|
|
109
|
-
let z = opt.and_then(|x| x.checked_add(1));
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## expect() Is Slightly Better
|
|
113
|
-
|
|
114
|
-
```rust
|
|
115
|
-
// unwrap() - no context
|
|
116
|
-
let file = File::open(path).unwrap();
|
|
117
|
-
// Panics with: "called `Result::unwrap()` on an `Err` value: Os { code: 2, ... }"
|
|
118
|
-
|
|
119
|
-
// expect() - adds context
|
|
120
|
-
let file = File::open(path)
|
|
121
|
-
.expect("config file should exist at startup");
|
|
122
|
-
// Panics with: "config file should exist at startup: Os { code: 2, ... }"
|
|
123
|
-
|
|
124
|
-
// But still use only for invariants, not error handling
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Clippy Lint
|
|
128
|
-
|
|
129
|
-
```rust
|
|
130
|
-
// Enable these lints to catch unwrap usage:
|
|
131
|
-
#![warn(clippy::unwrap_used)]
|
|
132
|
-
#![warn(clippy::expect_used)] // Stricter
|
|
133
|
-
|
|
134
|
-
// Or per-function:
|
|
135
|
-
#[allow(clippy::unwrap_used)]
|
|
136
|
-
fn tests_only() { }
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## See Also
|
|
140
|
-
|
|
141
|
-
- [err-question-mark](err-question-mark.md) - Use ? for propagation
|
|
142
|
-
- [err-result-over-panic](err-result-over-panic.md) - Return Result instead of panicking
|
|
143
|
-
- [anti-expect-lazy](anti-expect-lazy.md) - Don't use expect for recoverable errors
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# anti-vec-for-slice
|
|
2
|
-
|
|
3
|
-
> Don't accept &Vec<T> when &[T] works
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`&Vec<T>` is strictly less flexible than `&[T]`. A slice can be created from `Vec`, arrays, and other slice-like types. Accepting `&Vec<T>` forces callers to have exactly a `Vec`, preventing them from using arrays, slices, or other collections.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Forces callers to have a Vec
|
|
13
|
-
fn sum(numbers: &Vec<i32>) -> i32 {
|
|
14
|
-
numbers.iter().sum()
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Caller must allocate
|
|
18
|
-
let arr = [1, 2, 3, 4, 5];
|
|
19
|
-
sum(&arr.to_vec()); // Unnecessary allocation
|
|
20
|
-
|
|
21
|
-
// Slice won't work
|
|
22
|
-
let slice: &[i32] = &[1, 2, 3];
|
|
23
|
-
// sum(slice); // Error: expected &Vec<i32>
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Good
|
|
27
|
-
|
|
28
|
-
```rust
|
|
29
|
-
// Accept slice - works with Vec, arrays, slices
|
|
30
|
-
fn sum(numbers: &[i32]) -> i32 {
|
|
31
|
-
numbers.iter().sum()
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// All these work
|
|
35
|
-
sum(&[1, 2, 3, 4, 5]); // Array
|
|
36
|
-
sum(&vec![1, 2, 3]); // Vec
|
|
37
|
-
sum(&numbers[1..3]); // Slice of slice
|
|
38
|
-
sum(numbers.as_slice()); // Explicit slice
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Deref Coercion
|
|
42
|
-
|
|
43
|
-
`Vec<T>` implements `Deref<Target = [T]>`, so `&Vec<T>` automatically coerces to `&[T]`:
|
|
44
|
-
|
|
45
|
-
```rust
|
|
46
|
-
fn takes_slice(s: &[i32]) { }
|
|
47
|
-
|
|
48
|
-
let vec = vec![1, 2, 3];
|
|
49
|
-
takes_slice(&vec); // &Vec<i32> -> &[i32] via Deref
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Mutable Slices
|
|
53
|
-
|
|
54
|
-
Same applies to `&mut`:
|
|
55
|
-
|
|
56
|
-
```rust
|
|
57
|
-
// Bad
|
|
58
|
-
fn double(numbers: &mut Vec<i32>) {
|
|
59
|
-
for n in numbers.iter_mut() {
|
|
60
|
-
*n *= 2;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Good
|
|
65
|
-
fn double(numbers: &mut [i32]) {
|
|
66
|
-
for n in numbers.iter_mut() {
|
|
67
|
-
*n *= 2;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## When to Accept &Vec<T>
|
|
73
|
-
|
|
74
|
-
Rarely. Only when you need Vec-specific operations:
|
|
75
|
-
|
|
76
|
-
```rust
|
|
77
|
-
fn needs_capacity(v: &Vec<i32>) -> usize {
|
|
78
|
-
v.capacity() // Only Vec has capacity
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
fn might_grow(v: &mut Vec<i32>) {
|
|
82
|
-
v.push(42); // Slice can't push
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Pattern: Accepting Multiple Types
|
|
87
|
-
|
|
88
|
-
```rust
|
|
89
|
-
// Accept anything that can be viewed as a slice
|
|
90
|
-
fn process<T: AsRef<[u8]>>(data: T) {
|
|
91
|
-
let bytes: &[u8] = data.as_ref();
|
|
92
|
-
// ...
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
process(&[1u8, 2, 3]); // Array
|
|
96
|
-
process(vec![1u8, 2, 3]); // Vec
|
|
97
|
-
process(&some_vec); // &Vec
|
|
98
|
-
process(b"bytes"); // Byte string
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Similar Anti-patterns
|
|
102
|
-
|
|
103
|
-
| Anti-pattern | Better |
|
|
104
|
-
|--------------|--------|
|
|
105
|
-
| `&Vec<T>` | `&[T]` |
|
|
106
|
-
| `&String` | `&str` |
|
|
107
|
-
| `&PathBuf` | `&Path` |
|
|
108
|
-
| `&Box<T>` | `&T` |
|
|
109
|
-
|
|
110
|
-
## Clippy Detection
|
|
111
|
-
|
|
112
|
-
```toml
|
|
113
|
-
[lints.clippy]
|
|
114
|
-
ptr_arg = "warn" # Catches &Vec, &String, &PathBuf
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## See Also
|
|
118
|
-
|
|
119
|
-
- [anti-string-for-str](./anti-string-for-str.md) - Similar for String
|
|
120
|
-
- [own-slice-over-vec](./own-slice-over-vec.md) - Slice patterns
|
|
121
|
-
- [api-impl-asref](./api-impl-asref.md) - AsRef pattern
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# api-builder-must-use
|
|
2
|
-
|
|
3
|
-
> Mark builder methods with `#[must_use]` to prevent silent drops
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Builder pattern methods return a modified builder. Without `#[must_use]`, calling a builder method and ignoring the return value silently does nothing—the builder is dropped, and the configuration is lost. This creates confusing bugs where code appears correct but has no effect.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
struct RequestBuilder {
|
|
13
|
-
url: String,
|
|
14
|
-
timeout: Option<Duration>,
|
|
15
|
-
headers: Vec<(String, String)>,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
impl RequestBuilder {
|
|
19
|
-
fn timeout(mut self, duration: Duration) -> Self {
|
|
20
|
-
self.timeout = Some(duration);
|
|
21
|
-
self
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
fn header(mut self, key: &str, value: &str) -> Self {
|
|
25
|
-
self.headers.push((key.to_string(), value.to_string()));
|
|
26
|
-
self
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Bug: builder methods are ignored - no warning!
|
|
31
|
-
let request = RequestBuilder::new("https://api.example.com");
|
|
32
|
-
request.timeout(Duration::from_secs(30)); // Dropped silently!
|
|
33
|
-
request.header("Authorization", "Bearer token"); // Dropped silently!
|
|
34
|
-
let response = request.send(); // Sends with no timeout or headers
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Good
|
|
38
|
-
|
|
39
|
-
```rust
|
|
40
|
-
struct RequestBuilder {
|
|
41
|
-
url: String,
|
|
42
|
-
timeout: Option<Duration>,
|
|
43
|
-
headers: Vec<(String, String)>,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
impl RequestBuilder {
|
|
47
|
-
#[must_use = "builder methods return modified builder - chain or assign"]
|
|
48
|
-
fn timeout(mut self, duration: Duration) -> Self {
|
|
49
|
-
self.timeout = Some(duration);
|
|
50
|
-
self
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
#[must_use = "builder methods return modified builder - chain or assign"]
|
|
54
|
-
fn header(mut self, key: &str, value: &str) -> Self {
|
|
55
|
-
self.headers.push((key.to_string(), value.to_string()));
|
|
56
|
-
self
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Now warns: unused return value that must be used
|
|
61
|
-
let request = RequestBuilder::new("https://api.example.com");
|
|
62
|
-
request.timeout(Duration::from_secs(30)); // Warning!
|
|
63
|
-
|
|
64
|
-
// Correct: chain methods
|
|
65
|
-
let response = RequestBuilder::new("https://api.example.com")
|
|
66
|
-
.timeout(Duration::from_secs(30))
|
|
67
|
-
.header("Authorization", "Bearer token")
|
|
68
|
-
.send();
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Apply to Entire Type
|
|
72
|
-
|
|
73
|
-
```rust
|
|
74
|
-
#[must_use = "builders do nothing unless consumed"]
|
|
75
|
-
struct ConfigBuilder {
|
|
76
|
-
log_level: Level,
|
|
77
|
-
max_connections: usize,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Now all methods returning Self warn if ignored
|
|
81
|
-
impl ConfigBuilder {
|
|
82
|
-
fn log_level(mut self, level: Level) -> Self {
|
|
83
|
-
self.log_level = level;
|
|
84
|
-
self
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
fn max_connections(mut self, n: usize) -> Self {
|
|
88
|
-
self.max_connections = n;
|
|
89
|
-
self
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
fn build(self) -> Config {
|
|
93
|
-
Config {
|
|
94
|
-
log_level: self.log_level,
|
|
95
|
-
max_connections: self.max_connections,
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Message Guidelines
|
|
102
|
-
|
|
103
|
-
```rust
|
|
104
|
-
// Descriptive message helps users understand
|
|
105
|
-
#[must_use = "builder methods return modified builder"]
|
|
106
|
-
fn with_foo(self, foo: Foo) -> Self { ... }
|
|
107
|
-
|
|
108
|
-
#[must_use = "this creates a new String and does not modify the original"]
|
|
109
|
-
fn to_uppercase(&self) -> String { ... }
|
|
110
|
-
|
|
111
|
-
#[must_use = "iterator adaptors are lazy - use .collect() to consume"]
|
|
112
|
-
fn map<F>(self, f: F) -> Map<Self, F> { ... }
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Clippy Lint
|
|
116
|
-
|
|
117
|
-
```toml
|
|
118
|
-
[lints.clippy]
|
|
119
|
-
must_use_candidate = "warn" # Suggests where #[must_use] would help
|
|
120
|
-
return_self_not_must_use = "warn" # Specifically for -> Self methods
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Standard Library Examples
|
|
124
|
-
|
|
125
|
-
```rust
|
|
126
|
-
// std::Option - must_use on map, and, or
|
|
127
|
-
let x: Option<i32> = Some(5);
|
|
128
|
-
x.map(|v| v * 2); // Warning: unused return value
|
|
129
|
-
|
|
130
|
-
// std::Result - must_use on the type itself
|
|
131
|
-
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
|
|
132
|
-
pub enum Result<T, E> { ... }
|
|
133
|
-
|
|
134
|
-
// Iterator adaptors
|
|
135
|
-
let v = vec![1, 2, 3];
|
|
136
|
-
v.iter().map(|x| x * 2); // Warning: iterators are lazy
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## See Also
|
|
140
|
-
|
|
141
|
-
- [api-builder-pattern](./api-builder-pattern.md) - Builder pattern best practices
|
|
142
|
-
- [api-must-use](./api-must-use.md) - General must_use guidelines
|
|
143
|
-
- [err-result-over-panic](./err-result-over-panic.md) - Result types are must_use
|