agy-superpowers 5.2.2 → 5.2.3
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/scratch-scripts.md +37 -0
- package/template/agent/rules/superpowers.md +4 -51
- 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,145 +0,0 @@
|
|
|
1
|
-
# err-doc-errors
|
|
2
|
-
|
|
3
|
-
> Document error conditions with `# Errors` section in doc comments
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Users of your API need to know what can go wrong and why. The `# Errors` documentation section is the standard Rust convention for describing when a function returns `Err`. Good error documentation helps callers handle errors appropriately and understand the contract of your API.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
/// Loads a configuration from the specified path.
|
|
13
|
-
pub fn load_config(path: &Path) -> Result<Config, ConfigError> {
|
|
14
|
-
// No documentation of error conditions
|
|
15
|
-
// Caller must read source code to understand what can fail
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/// Parses and validates the input string.
|
|
19
|
-
///
|
|
20
|
-
/// Returns the parsed value. // What about errors?
|
|
21
|
-
pub fn parse_input(input: &str) -> Result<Value, ParseError> {
|
|
22
|
-
// ...
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Good
|
|
27
|
-
|
|
28
|
-
```rust
|
|
29
|
-
/// Loads a configuration from the specified path.
|
|
30
|
-
///
|
|
31
|
-
/// # Errors
|
|
32
|
-
///
|
|
33
|
-
/// Returns an error if:
|
|
34
|
-
/// - The file at `path` does not exist or cannot be read
|
|
35
|
-
/// - The file contents are not valid TOML
|
|
36
|
-
/// - Required configuration keys are missing
|
|
37
|
-
/// - Configuration values are out of valid ranges
|
|
38
|
-
///
|
|
39
|
-
/// # Examples
|
|
40
|
-
///
|
|
41
|
-
/// ```
|
|
42
|
-
/// # use mylib::{load_config, ConfigError};
|
|
43
|
-
/// # fn main() -> Result<(), ConfigError> {
|
|
44
|
-
/// let config = load_config("app.toml")?;
|
|
45
|
-
/// # Ok(())
|
|
46
|
-
/// # }
|
|
47
|
-
/// ```
|
|
48
|
-
pub fn load_config(path: &Path) -> Result<Config, ConfigError> {
|
|
49
|
-
// ...
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/// Parses and validates the input string as a positive integer.
|
|
53
|
-
///
|
|
54
|
-
/// # Errors
|
|
55
|
-
///
|
|
56
|
-
/// Returns [`ParseError::Empty`] if the input is empty.
|
|
57
|
-
/// Returns [`ParseError::InvalidFormat`] if the input contains non-digit characters.
|
|
58
|
-
/// Returns [`ParseError::Overflow`] if the value exceeds `i64::MAX`.
|
|
59
|
-
/// Returns [`ParseError::NotPositive`] if the value is zero or negative.
|
|
60
|
-
pub fn parse_positive_int(input: &str) -> Result<i64, ParseError> {
|
|
61
|
-
// ...
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Linking to Error Variants
|
|
66
|
-
|
|
67
|
-
```rust
|
|
68
|
-
/// Attempts to connect to the database.
|
|
69
|
-
///
|
|
70
|
-
/// # Errors
|
|
71
|
-
///
|
|
72
|
-
/// This function will return an error if:
|
|
73
|
-
///
|
|
74
|
-
/// - [`DbError::ConnectionFailed`] - The database server is unreachable
|
|
75
|
-
/// - [`DbError::AuthenticationFailed`] - Invalid credentials
|
|
76
|
-
/// - [`DbError::Timeout`] - Connection attempt exceeded timeout
|
|
77
|
-
/// - [`DbError::TlsError`] - TLS handshake failed
|
|
78
|
-
///
|
|
79
|
-
/// See [`DbError`] for more details on each variant.
|
|
80
|
-
pub fn connect(config: &DbConfig) -> Result<Connection, DbError> {
|
|
81
|
-
// ...
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Panic vs Error Documentation
|
|
86
|
-
|
|
87
|
-
```rust
|
|
88
|
-
/// Divides two numbers.
|
|
89
|
-
///
|
|
90
|
-
/// # Errors
|
|
91
|
-
///
|
|
92
|
-
/// Returns [`MathError::DivisionByZero`] if `divisor` is zero.
|
|
93
|
-
///
|
|
94
|
-
/// # Panics
|
|
95
|
-
///
|
|
96
|
-
/// Panics if called from a non-main thread (debug builds only).
|
|
97
|
-
pub fn divide(dividend: i64, divisor: i64) -> Result<i64, MathError> {
|
|
98
|
-
// ...
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Error Section Format Options
|
|
103
|
-
|
|
104
|
-
```rust
|
|
105
|
-
// Style 1: Bullet list (good for multiple conditions)
|
|
106
|
-
/// # Errors
|
|
107
|
-
///
|
|
108
|
-
/// Returns an error if:
|
|
109
|
-
/// - The file does not exist
|
|
110
|
-
/// - The file cannot be read
|
|
111
|
-
/// - The content is invalid UTF-8
|
|
112
|
-
|
|
113
|
-
// Style 2: Returns statements (good for mapping to variants)
|
|
114
|
-
/// # Errors
|
|
115
|
-
///
|
|
116
|
-
/// Returns [`Error::NotFound`] if the item doesn't exist.
|
|
117
|
-
/// Returns [`Error::PermissionDenied`] if access is forbidden.
|
|
118
|
-
|
|
119
|
-
// Style 3: Prose (good for complex conditions)
|
|
120
|
-
/// # Errors
|
|
121
|
-
///
|
|
122
|
-
/// This function returns an error when the input fails validation.
|
|
123
|
-
/// Validation includes checking that all required fields are present,
|
|
124
|
-
/// that numeric fields are within allowed ranges, and that string
|
|
125
|
-
/// fields match their expected formats.
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Clippy Lint
|
|
129
|
-
|
|
130
|
-
```toml
|
|
131
|
-
# Cargo.toml - require error documentation
|
|
132
|
-
[lints.clippy]
|
|
133
|
-
missing_errors_doc = "warn"
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
```rust
|
|
137
|
-
// This will warn without # Errors section
|
|
138
|
-
pub fn might_fail() -> Result<(), Error> { Ok(()) }
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
## See Also
|
|
142
|
-
|
|
143
|
-
- [doc-examples-section](./doc-examples-section.md) - Examples in documentation
|
|
144
|
-
- [err-thiserror-lib](./err-thiserror-lib.md) - Defining error types
|
|
145
|
-
- [api-must-use](./api-must-use.md) - Marking Results as must_use
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
# err-expect-bugs-only
|
|
2
|
-
|
|
3
|
-
> Use `expect()` only for invariants that indicate bugs, not user errors
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`expect()` is better than `unwrap()` because it provides context, but it still panics. Reserve it for situations where failure indicates a bug in your code—a violated invariant, not a user error or external failure. The message should explain why the invariant should hold, helping future developers understand and fix the bug.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// User input can legitimately fail - don't expect
|
|
13
|
-
fn parse_user_input(input: &str) -> Config {
|
|
14
|
-
serde_json::from_str(input)
|
|
15
|
-
.expect("Invalid JSON") // User error, not a bug!
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Network can fail - don't expect
|
|
19
|
-
fn fetch_data(url: &str) -> Data {
|
|
20
|
-
reqwest::get(url)
|
|
21
|
-
.expect("Network request failed") // External failure!
|
|
22
|
-
.json()
|
|
23
|
-
.expect("Invalid response")
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// File might not exist - don't expect
|
|
27
|
-
fn load_config() -> Config {
|
|
28
|
-
let content = fs::read_to_string("config.json")
|
|
29
|
-
.expect("Config file missing"); // Environment issue!
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Good
|
|
34
|
-
|
|
35
|
-
```rust
|
|
36
|
-
// Invariant: after insert, key exists
|
|
37
|
-
fn cache_and_get(&mut self, key: String, value: Value) -> &Value {
|
|
38
|
-
self.cache.insert(key.clone(), value);
|
|
39
|
-
self.cache.get(&key)
|
|
40
|
-
.expect("BUG: key must exist immediately after insert")
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Invariant: regex is compile-time constant
|
|
44
|
-
fn create_parser() -> Regex {
|
|
45
|
-
Regex::new(r"^\d{4}-\d{2}-\d{2}$")
|
|
46
|
-
.expect("BUG: date regex is invalid - this is a compile-time constant")
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Invariant: already validated
|
|
50
|
-
fn process_validated(data: ValidatedData) -> Result<Output, ProcessError> {
|
|
51
|
-
let value = data.required_field
|
|
52
|
-
.expect("BUG: ValidatedData guarantees required_field is Some");
|
|
53
|
-
// ...
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Invariant: type system guarantees
|
|
57
|
-
fn get_first<T>(vec: Vec<T>) -> T
|
|
58
|
-
where
|
|
59
|
-
Vec<T>: NonEmpty, // Hypothetical trait
|
|
60
|
-
{
|
|
61
|
-
vec.into_iter().next()
|
|
62
|
-
.expect("BUG: NonEmpty Vec cannot be empty")
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## expect() Message Guidelines
|
|
67
|
-
|
|
68
|
-
Messages should:
|
|
69
|
-
1. Start with "BUG:" or similar to indicate it's an invariant
|
|
70
|
-
2. Explain WHY the invariant should hold
|
|
71
|
-
3. Help developers fix the issue
|
|
72
|
-
|
|
73
|
-
```rust
|
|
74
|
-
// ❌ Bad messages
|
|
75
|
-
.expect("failed") // No context
|
|
76
|
-
.expect("should not be None") // Doesn't explain why
|
|
77
|
-
.expect("Invalid state") // Vague
|
|
78
|
-
|
|
79
|
-
// ✅ Good messages
|
|
80
|
-
.expect("BUG: HashMap entry exists after insert")
|
|
81
|
-
.expect("BUG: validated input must parse - validation is broken")
|
|
82
|
-
.expect("BUG: static regex compilation failed - regex syntax error in source")
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Pattern: Validate Once, expect() After
|
|
86
|
-
|
|
87
|
-
```rust
|
|
88
|
-
struct ValidatedEmail(String);
|
|
89
|
-
|
|
90
|
-
impl ValidatedEmail {
|
|
91
|
-
pub fn new(email: &str) -> Result<Self, EmailError> {
|
|
92
|
-
// Validation happens here, returns Result
|
|
93
|
-
if !is_valid_email(email) {
|
|
94
|
-
return Err(EmailError::Invalid);
|
|
95
|
-
}
|
|
96
|
-
Ok(ValidatedEmail(email.to_string()))
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
pub fn domain(&self) -> &str {
|
|
100
|
-
// After validation, expect() is fine
|
|
101
|
-
self.0.split('@').nth(1)
|
|
102
|
-
.expect("BUG: ValidatedEmail must contain @")
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Alternatives When expect() Is Wrong
|
|
108
|
-
|
|
109
|
-
```rust
|
|
110
|
-
// Don't: expect on user data
|
|
111
|
-
let port: u16 = input.parse().expect("Invalid port");
|
|
112
|
-
|
|
113
|
-
// Do: Return Result
|
|
114
|
-
let port: u16 = input.parse().map_err(|_| ConfigError::InvalidPort)?;
|
|
115
|
-
|
|
116
|
-
// Do: Provide default
|
|
117
|
-
let port: u16 = input.parse().unwrap_or(8080);
|
|
118
|
-
|
|
119
|
-
// Do: Handle explicitly
|
|
120
|
-
let port: u16 = match input.parse() {
|
|
121
|
-
Ok(p) => p,
|
|
122
|
-
Err(_) => {
|
|
123
|
-
log::warn!("Invalid port '{}', using default", input);
|
|
124
|
-
8080
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## See Also
|
|
130
|
-
|
|
131
|
-
- [err-no-unwrap-prod](./err-no-unwrap-prod.md) - Avoiding unwrap in production
|
|
132
|
-
- [err-result-over-panic](./err-result-over-panic.md) - When to return Result
|
|
133
|
-
- [api-parse-dont-validate](./api-parse-dont-validate.md) - Type-driven validation
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# err-from-impl
|
|
2
|
-
|
|
3
|
-
> Implement `From<E>` for error conversions to enable `?` operator
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
The `?` operator automatically converts errors using `From` trait. By implementing `From<SourceError> for YourError`, you enable seamless error propagation without explicit `.map_err()` calls. This makes error handling code cleaner and ensures consistent error wrapping throughout your codebase.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
#[derive(Debug)]
|
|
13
|
-
enum AppError {
|
|
14
|
-
Io(std::io::Error),
|
|
15
|
-
Parse(serde_json::Error),
|
|
16
|
-
Database(diesel::result::Error),
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
fn load_config(path: &str) -> Result<Config, AppError> {
|
|
20
|
-
let content = std::fs::read_to_string(path)
|
|
21
|
-
.map_err(|e| AppError::Io(e))?; // Manual conversion everywhere
|
|
22
|
-
|
|
23
|
-
let config: Config = serde_json::from_str(&content)
|
|
24
|
-
.map_err(|e| AppError::Parse(e))?; // Repeated boilerplate
|
|
25
|
-
|
|
26
|
-
save_to_db(&config)
|
|
27
|
-
.map_err(|e| AppError::Database(e))?; // Gets tedious
|
|
28
|
-
|
|
29
|
-
Ok(config)
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Good
|
|
34
|
-
|
|
35
|
-
```rust
|
|
36
|
-
#[derive(Debug)]
|
|
37
|
-
enum AppError {
|
|
38
|
-
Io(std::io::Error),
|
|
39
|
-
Parse(serde_json::Error),
|
|
40
|
-
Database(diesel::result::Error),
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Implement From for each source error type
|
|
44
|
-
impl From<std::io::Error> for AppError {
|
|
45
|
-
fn from(err: std::io::Error) -> Self {
|
|
46
|
-
AppError::Io(err)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
impl From<serde_json::Error> for AppError {
|
|
51
|
-
fn from(err: serde_json::Error) -> Self {
|
|
52
|
-
AppError::Parse(err)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
impl From<diesel::result::Error> for AppError {
|
|
57
|
-
fn from(err: diesel::result::Error) -> Self {
|
|
58
|
-
AppError::Database(err)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fn load_config(path: &str) -> Result<Config, AppError> {
|
|
63
|
-
let content = std::fs::read_to_string(path)?; // Auto-converts
|
|
64
|
-
let config: Config = serde_json::from_str(&content)?; // Clean!
|
|
65
|
-
save_to_db(&config)?;
|
|
66
|
-
Ok(config)
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Use thiserror for Automatic From
|
|
71
|
-
|
|
72
|
-
```rust
|
|
73
|
-
use thiserror::Error;
|
|
74
|
-
|
|
75
|
-
#[derive(Error, Debug)]
|
|
76
|
-
enum AppError {
|
|
77
|
-
#[error("IO error: {0}")]
|
|
78
|
-
Io(#[from] std::io::Error), // Auto-generates From impl
|
|
79
|
-
|
|
80
|
-
#[error("Parse error: {0}")]
|
|
81
|
-
Parse(#[from] serde_json::Error), // #[from] does the work
|
|
82
|
-
|
|
83
|
-
#[error("Database error: {0}")]
|
|
84
|
-
Database(#[from] diesel::result::Error),
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Now ? just works
|
|
88
|
-
fn load_config(path: &str) -> Result<Config, AppError> {
|
|
89
|
-
let content = std::fs::read_to_string(path)?;
|
|
90
|
-
let config: Config = serde_json::from_str(&content)?;
|
|
91
|
-
save_to_db(&config)?;
|
|
92
|
-
Ok(config)
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## From with Context
|
|
97
|
-
|
|
98
|
-
Sometimes you need to add context during conversion:
|
|
99
|
-
|
|
100
|
-
```rust
|
|
101
|
-
#[derive(Error, Debug)]
|
|
102
|
-
enum ConfigError {
|
|
103
|
-
#[error("Failed to read config from '{path}': {source}")]
|
|
104
|
-
ReadFailed {
|
|
105
|
-
path: String,
|
|
106
|
-
#[source]
|
|
107
|
-
source: std::io::Error,
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Can't use #[from] when you need extra context
|
|
112
|
-
fn load_config(path: &str) -> Result<Config, ConfigError> {
|
|
113
|
-
let content = std::fs::read_to_string(path)
|
|
114
|
-
.map_err(|source| ConfigError::ReadFailed {
|
|
115
|
-
path: path.to_string(),
|
|
116
|
-
source,
|
|
117
|
-
})?;
|
|
118
|
-
// ...
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Or use anyhow for ad-hoc context
|
|
122
|
-
use anyhow::{Context, Result};
|
|
123
|
-
|
|
124
|
-
fn load_config(path: &str) -> Result<Config> {
|
|
125
|
-
let content = std::fs::read_to_string(path)
|
|
126
|
-
.with_context(|| format!("Failed to read config from '{}'", path))?;
|
|
127
|
-
// ...
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## Blanket From Implementations
|
|
132
|
-
|
|
133
|
-
Be careful with blanket implementations:
|
|
134
|
-
|
|
135
|
-
```rust
|
|
136
|
-
// ❌ Too broad - conflicts with other From impls
|
|
137
|
-
impl<E: std::error::Error> From<E> for AppError {
|
|
138
|
-
fn from(err: E) -> Self {
|
|
139
|
-
AppError::Other(err.to_string())
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ✅ Specific implementations
|
|
144
|
-
impl From<std::io::Error> for AppError { ... }
|
|
145
|
-
impl From<ParseIntError> for AppError { ... }
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## See Also
|
|
149
|
-
|
|
150
|
-
- [err-thiserror-lib](./err-thiserror-lib.md) - Using thiserror for libraries
|
|
151
|
-
- [err-source-chain](./err-source-chain.md) - Preserving error chains
|
|
152
|
-
- [err-question-mark](./err-question-mark.md) - The ? operator
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# err-lowercase-msg
|
|
2
|
-
|
|
3
|
-
> Start error messages lowercase, no trailing punctuation
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Error messages are often chained, logged, or displayed with additional context. Consistent formatting—lowercase start, no trailing period—allows clean composition: "failed to load config: invalid JSON: unexpected token". Mixed case and punctuation create awkward output: "Failed to load config.: Invalid JSON.: Unexpected token.".
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
use thiserror::Error;
|
|
13
|
-
|
|
14
|
-
#[derive(Error, Debug)]
|
|
15
|
-
enum ConfigError {
|
|
16
|
-
#[error("Failed to read config file.")] // Capital F, trailing period
|
|
17
|
-
ReadFailed(#[from] std::io::Error),
|
|
18
|
-
|
|
19
|
-
#[error("Invalid JSON format!")] // Capital I, exclamation
|
|
20
|
-
ParseFailed(#[from] serde_json::Error),
|
|
21
|
-
|
|
22
|
-
#[error("The requested key was not found")] // Reads like a sentence
|
|
23
|
-
KeyNotFound(String),
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Chained output: "Config load error: Failed to read config file.: No such file"
|
|
27
|
-
// Awkward capitalization and punctuation
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Good
|
|
31
|
-
|
|
32
|
-
```rust
|
|
33
|
-
use thiserror::Error;
|
|
34
|
-
|
|
35
|
-
#[derive(Error, Debug)]
|
|
36
|
-
enum ConfigError {
|
|
37
|
-
#[error("failed to read config file")] // lowercase, no period
|
|
38
|
-
ReadFailed(#[from] std::io::Error),
|
|
39
|
-
|
|
40
|
-
#[error("invalid JSON format")] // lowercase, no period
|
|
41
|
-
ParseFailed(#[from] serde_json::Error),
|
|
42
|
-
|
|
43
|
-
#[error("key not found: {0}")] // lowercase, data at end
|
|
44
|
-
KeyNotFound(String),
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Chained output: "config load error: failed to read config file: no such file"
|
|
48
|
-
// Clean, consistent
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Rust Standard Library Convention
|
|
52
|
-
|
|
53
|
-
The standard library follows this convention:
|
|
54
|
-
|
|
55
|
-
```rust
|
|
56
|
-
// std::io::Error messages
|
|
57
|
-
"entity not found"
|
|
58
|
-
"permission denied"
|
|
59
|
-
"connection refused"
|
|
60
|
-
|
|
61
|
-
// std::num::ParseIntError
|
|
62
|
-
"invalid digit found in string"
|
|
63
|
-
|
|
64
|
-
// std::str::Utf8Error
|
|
65
|
-
"invalid utf-8 sequence"
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Formatting Guidelines
|
|
69
|
-
|
|
70
|
-
| Do | Don't |
|
|
71
|
-
|----|-------|
|
|
72
|
-
| `"failed to parse config"` | `"Failed to parse config."` |
|
|
73
|
-
| `"invalid input: expected number"` | `"Invalid input - expected a number!"` |
|
|
74
|
-
| `"connection timed out after {0}s"` | `"Connection Timed Out After {0} seconds."` |
|
|
75
|
-
| `"key '{0}' not found"` | `"Key Not Found: {0}"` |
|
|
76
|
-
|
|
77
|
-
## Context Addition Pattern
|
|
78
|
-
|
|
79
|
-
```rust
|
|
80
|
-
use anyhow::{Context, Result};
|
|
81
|
-
|
|
82
|
-
fn load_user(id: u64) -> Result<User> {
|
|
83
|
-
let data = fetch(id)
|
|
84
|
-
.with_context(|| format!("failed to fetch user {}", id))?;
|
|
85
|
-
|
|
86
|
-
parse_user(data)
|
|
87
|
-
.with_context(|| "failed to parse user data")?
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Output: "failed to fetch user 42: connection refused"
|
|
91
|
-
// All lowercase, clean chain
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Display vs Debug
|
|
95
|
-
|
|
96
|
-
```rust
|
|
97
|
-
#[derive(Error, Debug)]
|
|
98
|
-
#[error("invalid configuration")] // Display: for users/logs
|
|
99
|
-
pub struct ConfigError {
|
|
100
|
-
path: PathBuf,
|
|
101
|
-
source: io::Error,
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Debug output (for developers) can have more detail
|
|
105
|
-
// Display output (for users) should be clean
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## When to Use Capitals
|
|
109
|
-
|
|
110
|
-
```rust
|
|
111
|
-
// Proper nouns / acronyms keep their case
|
|
112
|
-
#[error("invalid JSON syntax")] // JSON is an acronym
|
|
113
|
-
#[error("OAuth token expired")] // OAuth is a proper noun
|
|
114
|
-
#[error("HTTP request failed")] // HTTP is an acronym
|
|
115
|
-
|
|
116
|
-
// Error codes can be uppercase
|
|
117
|
-
#[error("error code E0001: invalid input")]
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## See Also
|
|
121
|
-
|
|
122
|
-
- [err-thiserror-lib](./err-thiserror-lib.md) - Error definition with thiserror
|
|
123
|
-
- [err-context-chain](./err-context-chain.md) - Adding context to errors
|
|
124
|
-
- [doc-examples-section](./doc-examples-section.md) - Documentation conventions
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# err-no-unwrap-prod
|
|
2
|
-
|
|
3
|
-
> Avoid `unwrap()` in production code; use `?`, `expect()`, or handle errors
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`unwrap()` panics on `None` or `Err` without any context about what went wrong. In production, this creates cryptic crash messages that are hard to debug. Either propagate errors with `?`, use `expect()` with a message explaining the invariant, or handle the error explicitly.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
fn process_request(req: Request) -> Response {
|
|
13
|
-
let user_id = req.headers.get("X-User-Id").unwrap(); // Why did it fail?
|
|
14
|
-
let user = database.find_user(user_id).unwrap(); // Which operation?
|
|
15
|
-
let data = user.preferences.get("theme").unwrap(); // No context
|
|
16
|
-
|
|
17
|
-
Response::new(data)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Crash message: "called `Option::unwrap()` on a `None` value"
|
|
21
|
-
// Where? Why? No idea.
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Good
|
|
25
|
-
|
|
26
|
-
```rust
|
|
27
|
-
// Option 1: Propagate with ?
|
|
28
|
-
fn process_request(req: Request) -> Result<Response, AppError> {
|
|
29
|
-
let user_id = req.headers
|
|
30
|
-
.get("X-User-Id")
|
|
31
|
-
.ok_or(AppError::MissingHeader("X-User-Id"))?;
|
|
32
|
-
|
|
33
|
-
let user = database.find_user(user_id)?;
|
|
34
|
-
|
|
35
|
-
let data = user.preferences
|
|
36
|
-
.get("theme")
|
|
37
|
-
.ok_or(AppError::MissingPreference("theme"))?;
|
|
38
|
-
|
|
39
|
-
Ok(Response::new(data))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Option 2: expect() for invariants (not user input)
|
|
43
|
-
fn get_config_value(&self, key: &str) -> &str {
|
|
44
|
-
self.config
|
|
45
|
-
.get(key)
|
|
46
|
-
.expect("BUG: required config key missing after validation")
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Option 3: Provide defaults
|
|
50
|
-
fn get_theme(user: &User) -> &str {
|
|
51
|
-
user.preferences
|
|
52
|
-
.get("theme")
|
|
53
|
-
.unwrap_or(&"default")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Option 4: Match for complex handling
|
|
57
|
-
fn process_optional(value: Option<Data>) -> ProcessedData {
|
|
58
|
-
match value {
|
|
59
|
-
Some(data) => process(data),
|
|
60
|
-
None => {
|
|
61
|
-
log::warn!("No data provided, using fallback");
|
|
62
|
-
ProcessedData::default()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## `expect()` vs `unwrap()`
|
|
69
|
-
|
|
70
|
-
```rust
|
|
71
|
-
// Bad: no context
|
|
72
|
-
let port = config.get("port").unwrap();
|
|
73
|
-
|
|
74
|
-
// Better: explains the invariant
|
|
75
|
-
let port = config.get("port")
|
|
76
|
-
.expect("config must contain 'port' after validation");
|
|
77
|
-
|
|
78
|
-
// Best: propagate if it's not truly an invariant
|
|
79
|
-
let port = config.get("port")
|
|
80
|
-
.ok_or_else(|| ConfigError::MissingKey("port"))?;
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Alternatives to unwrap()
|
|
84
|
-
|
|
85
|
-
| Situation | Use Instead |
|
|
86
|
-
|-----------|-------------|
|
|
87
|
-
| Can propagate error | `?` operator |
|
|
88
|
-
| Has sensible default | `unwrap_or()`, `unwrap_or_default()` |
|
|
89
|
-
| Default requires computation | `unwrap_or_else(\|\| ...)` |
|
|
90
|
-
| Internal invariant | `expect("explanation")` |
|
|
91
|
-
| Need to handle both cases | `match` or `if let` |
|
|
92
|
-
|
|
93
|
-
## Clippy Lints
|
|
94
|
-
|
|
95
|
-
```toml
|
|
96
|
-
# Cargo.toml
|
|
97
|
-
[lints.clippy]
|
|
98
|
-
unwrap_used = "warn" # Warn on unwrap()
|
|
99
|
-
expect_used = "warn" # Also warn on expect() (stricter)
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
```rust
|
|
103
|
-
// Allow in specific places where it's justified
|
|
104
|
-
#[allow(clippy::unwrap_used)]
|
|
105
|
-
fn definitely_safe() {
|
|
106
|
-
// Unwrap is safe here because...
|
|
107
|
-
let x = Some(5).unwrap();
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## See Also
|
|
112
|
-
|
|
113
|
-
- [err-result-over-panic](./err-result-over-panic.md) - Return Result instead of panicking
|
|
114
|
-
- [err-expect-bugs-only](./err-expect-bugs-only.md) - When expect() is appropriate
|
|
115
|
-
- [anti-unwrap-abuse](./anti-unwrap-abuse.md) - Patterns for avoiding unwrap
|