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,151 +0,0 @@
|
|
|
1
|
-
# err-question-mark
|
|
2
|
-
|
|
3
|
-
> Use `?` operator for clean propagation
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
The `?` operator is Rust's idiomatic way to propagate errors. It's concise, readable, and automatically converts between compatible error types using `From`. It replaces verbose `match` or `unwrap()` calls.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Verbose match-based error handling
|
|
13
|
-
fn load_config() -> Result<Config, Error> {
|
|
14
|
-
let content = match std::fs::read_to_string("config.toml") {
|
|
15
|
-
Ok(c) => c,
|
|
16
|
-
Err(e) => return Err(Error::Io(e)),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
let config = match toml::from_str(&content) {
|
|
20
|
-
Ok(c) => c,
|
|
21
|
-
Err(e) => return Err(Error::Parse(e)),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
Ok(config)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Or worse - using unwrap
|
|
28
|
-
fn load_config_bad() -> Config {
|
|
29
|
-
let content = std::fs::read_to_string("config.toml").unwrap();
|
|
30
|
-
toml::from_str(&content).unwrap()
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Good
|
|
35
|
-
|
|
36
|
-
```rust
|
|
37
|
-
fn load_config() -> Result<Config, Error> {
|
|
38
|
-
let content = std::fs::read_to_string("config.toml")?;
|
|
39
|
-
let config = toml::from_str(&content)?;
|
|
40
|
-
Ok(config)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Even more concise
|
|
44
|
-
fn load_config() -> Result<Config, Error> {
|
|
45
|
-
Ok(toml::from_str(&std::fs::read_to_string("config.toml")?)?)
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## How ? Works
|
|
50
|
-
|
|
51
|
-
```rust
|
|
52
|
-
// This:
|
|
53
|
-
let x = expr?;
|
|
54
|
-
|
|
55
|
-
// Expands roughly to:
|
|
56
|
-
let x = match expr {
|
|
57
|
-
Ok(val) => val,
|
|
58
|
-
Err(err) => return Err(From::from(err)),
|
|
59
|
-
};
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Combining with Context
|
|
63
|
-
|
|
64
|
-
```rust
|
|
65
|
-
use anyhow::{Context, Result};
|
|
66
|
-
|
|
67
|
-
fn load_user(id: u64) -> Result<User> {
|
|
68
|
-
let path = format!("users/{}.json", id);
|
|
69
|
-
|
|
70
|
-
let content = std::fs::read_to_string(&path)
|
|
71
|
-
.with_context(|| format!("failed to read user file: {}", path))?;
|
|
72
|
-
|
|
73
|
-
let user: User = serde_json::from_str(&content)
|
|
74
|
-
.context("failed to parse user JSON")?;
|
|
75
|
-
|
|
76
|
-
Ok(user)
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## ? with Option
|
|
81
|
-
|
|
82
|
-
```rust
|
|
83
|
-
fn get_first_word(text: &str) -> Option<&str> {
|
|
84
|
-
let first_line = text.lines().next()?;
|
|
85
|
-
let first_word = first_line.split_whitespace().next()?;
|
|
86
|
-
Some(first_word)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Convert Option to Result
|
|
90
|
-
fn get_required_config(key: &str) -> Result<String, Error> {
|
|
91
|
-
config.get(key)
|
|
92
|
-
.cloned()
|
|
93
|
-
.ok_or_else(|| Error::MissingConfig(key.to_string()))
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Error Type Conversion
|
|
98
|
-
|
|
99
|
-
```rust
|
|
100
|
-
use thiserror::Error;
|
|
101
|
-
|
|
102
|
-
#[derive(Error, Debug)]
|
|
103
|
-
enum MyError {
|
|
104
|
-
#[error("io error")]
|
|
105
|
-
Io(#[from] std::io::Error), // Auto From impl
|
|
106
|
-
|
|
107
|
-
#[error("parse error")]
|
|
108
|
-
Parse(#[from] serde_json::Error), // Auto From impl
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
fn process() -> Result<(), MyError> {
|
|
112
|
-
// ? automatically converts io::Error to MyError via From
|
|
113
|
-
let content = std::fs::read_to_string("file.txt")?;
|
|
114
|
-
|
|
115
|
-
// ? automatically converts serde_json::Error to MyError
|
|
116
|
-
let data: Data = serde_json::from_str(&content)?;
|
|
117
|
-
|
|
118
|
-
Ok(())
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## In main()
|
|
123
|
-
|
|
124
|
-
```rust
|
|
125
|
-
// Option 1: Return Result from main
|
|
126
|
-
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
127
|
-
let config = load_config()?;
|
|
128
|
-
run_app(config)?;
|
|
129
|
-
Ok(())
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Option 2: Handle in main, exit on error
|
|
133
|
-
fn main() {
|
|
134
|
-
if let Err(e) = run() {
|
|
135
|
-
eprintln!("Error: {:#}", e);
|
|
136
|
-
std::process::exit(1);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
fn run() -> anyhow::Result<()> {
|
|
141
|
-
let config = load_config()?;
|
|
142
|
-
run_app(config)?;
|
|
143
|
-
Ok(())
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## See Also
|
|
148
|
-
|
|
149
|
-
- [err-context-chain](err-context-chain.md) - Add context with .context()
|
|
150
|
-
- [err-from-impl](err-from-impl.md) - Use #[from] for automatic conversion
|
|
151
|
-
- [err-anyhow-app](err-anyhow-app.md) - Use anyhow for applications
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# err-result-over-panic
|
|
2
|
-
|
|
3
|
-
> Return `Result<T, E>` instead of panicking for recoverable errors
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Panics unwind the stack and crash the thread (or program). They're unrecoverable from the caller's perspective. `Result<T, E>` gives callers the ability to decide how to handle errors—retry, fallback, propagate, or log. Libraries should almost never panic; applications should minimize panics to truly unrecoverable situations.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
fn parse_config(path: &str) -> Config {
|
|
13
|
-
let content = std::fs::read_to_string(path)
|
|
14
|
-
.expect("Failed to read config"); // Crashes on missing file
|
|
15
|
-
|
|
16
|
-
serde_json::from_str(&content)
|
|
17
|
-
.expect("Invalid config format") // Crashes on bad JSON
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
fn divide(a: i32, b: i32) -> i32 {
|
|
21
|
-
if b == 0 {
|
|
22
|
-
panic!("Division by zero!"); // Crashes the program
|
|
23
|
-
}
|
|
24
|
-
a / b
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Caller has no chance to recover or provide a fallback.
|
|
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: {0}")]
|
|
38
|
-
Io(#[from] std::io::Error),
|
|
39
|
-
#[error("Invalid config format: {0}")]
|
|
40
|
-
Parse(#[from] serde_json::Error),
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
fn parse_config(path: &str) -> Result<Config, ConfigError> {
|
|
44
|
-
let content = std::fs::read_to_string(path)?;
|
|
45
|
-
let config = serde_json::from_str(&content)?;
|
|
46
|
-
Ok(config)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
|
|
50
|
-
if b == 0 {
|
|
51
|
-
return Err("Division by zero");
|
|
52
|
-
}
|
|
53
|
-
Ok(a / b)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Caller decides how to handle
|
|
57
|
-
match parse_config("app.json") {
|
|
58
|
-
Ok(config) => run_app(config),
|
|
59
|
-
Err(e) => {
|
|
60
|
-
eprintln!("Using default config: {}", e);
|
|
61
|
-
run_app(Config::default())
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## When Panic IS Appropriate
|
|
67
|
-
|
|
68
|
-
```rust
|
|
69
|
-
// 1. Bug in the program (invariant violation)
|
|
70
|
-
fn get_cached_value(&self, key: &str) -> &Value {
|
|
71
|
-
self.cache.get(key).expect("BUG: key was verified to exist")
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 2. Setup/initialization that can't reasonably fail
|
|
75
|
-
fn main() {
|
|
76
|
-
let config = Config::load().expect("Failed to load required config");
|
|
77
|
-
// Can't run without config, panic is reasonable
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 3. Tests
|
|
81
|
-
#[test]
|
|
82
|
-
fn test_parse() {
|
|
83
|
-
let result = parse("valid input").unwrap(); // unwrap OK in tests
|
|
84
|
-
assert_eq!(result, expected);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 4. Examples and prototypes
|
|
88
|
-
fn main() {
|
|
89
|
-
// Quick prototype, panic is fine
|
|
90
|
-
let data = fetch_data().unwrap();
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Panic vs Result Decision Guide
|
|
95
|
-
|
|
96
|
-
| Situation | Use |
|
|
97
|
-
|-----------|-----|
|
|
98
|
-
| File not found | `Result` |
|
|
99
|
-
| Network error | `Result` |
|
|
100
|
-
| Invalid user input | `Result` |
|
|
101
|
-
| Parse error | `Result` |
|
|
102
|
-
| Index out of bounds (from user data) | `Result` |
|
|
103
|
-
| Index out of bounds (internal bug) | Panic |
|
|
104
|
-
| Violated internal invariant | Panic |
|
|
105
|
-
| Unimplemented code path | Panic (`unimplemented!()`) |
|
|
106
|
-
| Impossible state reached | Panic (`unreachable!()`) |
|
|
107
|
-
|
|
108
|
-
## Library vs Application
|
|
109
|
-
|
|
110
|
-
```rust
|
|
111
|
-
// Library: NEVER panic on user input
|
|
112
|
-
pub fn parse(input: &str) -> Result<Ast, ParseError> {
|
|
113
|
-
// Always return Result
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Application: Can panic at top level for critical failures
|
|
117
|
-
fn main() {
|
|
118
|
-
if let Err(e) = run() {
|
|
119
|
-
eprintln!("Fatal error: {}", e);
|
|
120
|
-
std::process::exit(1);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## See Also
|
|
126
|
-
|
|
127
|
-
- [err-thiserror-lib](./err-thiserror-lib.md) - Define error types for libraries
|
|
128
|
-
- [err-anyhow-app](./err-anyhow-app.md) - Ergonomic errors for applications
|
|
129
|
-
- [err-no-unwrap-prod](./err-no-unwrap-prod.md) - Avoid unwrap in production code
|
|
130
|
-
- [anti-unwrap-abuse](./anti-unwrap-abuse.md) - When unwrap is acceptable
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
# err-source-chain
|
|
2
|
-
|
|
3
|
-
> Preserve error chains with `#[source]` or `source()` method
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Errors often have underlying causes. Preserving the error chain (via `source()` method) allows logging frameworks and error reporters to show the full context: "config parse failed → JSON syntax error at line 5 → unexpected token". Without chaining, you lose valuable debugging information.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
#[derive(Debug)]
|
|
13
|
-
enum ConfigError {
|
|
14
|
-
ParseFailed(String), // Lost the original serde_json::Error
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fn load_config(path: &str) -> Result<Config, ConfigError> {
|
|
18
|
-
let content = std::fs::read_to_string(path)
|
|
19
|
-
.map_err(|e| ConfigError::ParseFailed(e.to_string()))?; // Chain lost!
|
|
20
|
-
|
|
21
|
-
serde_json::from_str(&content)
|
|
22
|
-
.map_err(|e| ConfigError::ParseFailed(e.to_string()))? // No source
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Error output: "Parse failed: invalid type: ..."
|
|
26
|
-
// Missing: which file? what line? what was the parent error?
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Good
|
|
30
|
-
|
|
31
|
-
```rust
|
|
32
|
-
use thiserror::Error;
|
|
33
|
-
|
|
34
|
-
#[derive(Error, Debug)]
|
|
35
|
-
enum ConfigError {
|
|
36
|
-
#[error("Failed to read config file '{path}'")]
|
|
37
|
-
ReadFailed {
|
|
38
|
-
path: String,
|
|
39
|
-
#[source] // Preserves the error chain
|
|
40
|
-
source: std::io::Error,
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
#[error("Failed to parse config file '{path}'")]
|
|
44
|
-
ParseFailed {
|
|
45
|
-
path: String,
|
|
46
|
-
#[source] // Original parse error preserved
|
|
47
|
-
source: serde_json::Error,
|
|
48
|
-
},
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
fn load_config(path: &str) -> Result<Config, ConfigError> {
|
|
52
|
-
let content = std::fs::read_to_string(path)
|
|
53
|
-
.map_err(|source| ConfigError::ReadFailed {
|
|
54
|
-
path: path.to_string(),
|
|
55
|
-
source, // Chain preserved
|
|
56
|
-
})?;
|
|
57
|
-
|
|
58
|
-
serde_json::from_str(&content)
|
|
59
|
-
.map_err(|source| ConfigError::ParseFailed {
|
|
60
|
-
path: path.to_string(),
|
|
61
|
-
source,
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Manual source() Implementation
|
|
67
|
-
|
|
68
|
-
```rust
|
|
69
|
-
use std::error::Error;
|
|
70
|
-
|
|
71
|
-
#[derive(Debug)]
|
|
72
|
-
struct MyError {
|
|
73
|
-
message: String,
|
|
74
|
-
source: Option<Box<dyn Error + Send + Sync>>,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
impl std::fmt::Display for MyError {
|
|
78
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
79
|
-
write!(f, "{}", self.message)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
impl Error for MyError {
|
|
84
|
-
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
85
|
-
self.source.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static))
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Walking the Error Chain
|
|
91
|
-
|
|
92
|
-
```rust
|
|
93
|
-
fn print_error_chain(error: &dyn std::error::Error) {
|
|
94
|
-
eprintln!("Error: {}", error);
|
|
95
|
-
|
|
96
|
-
let mut source = error.source();
|
|
97
|
-
while let Some(err) = source {
|
|
98
|
-
eprintln!("Caused by: {}", err);
|
|
99
|
-
source = err.source();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// With anyhow, use {:?} for full chain
|
|
104
|
-
let result: anyhow::Result<()> = do_something();
|
|
105
|
-
if let Err(e) = result {
|
|
106
|
-
eprintln!("{:?}", e); // Prints full chain with backtraces
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## anyhow Context
|
|
111
|
-
|
|
112
|
-
```rust
|
|
113
|
-
use anyhow::{Context, Result};
|
|
114
|
-
|
|
115
|
-
fn load_config(path: &str) -> Result<Config> {
|
|
116
|
-
let content = std::fs::read_to_string(path)
|
|
117
|
-
.with_context(|| format!("Failed to read '{}'", path))?;
|
|
118
|
-
|
|
119
|
-
let config: Config = serde_json::from_str(&content)
|
|
120
|
-
.with_context(|| format!("Failed to parse '{}'", path))?;
|
|
121
|
-
|
|
122
|
-
Ok(config)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Output:
|
|
126
|
-
// Error: Failed to parse 'config.json'
|
|
127
|
-
// Caused by: expected `:` at line 5 column 10
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## #[from] vs #[source]
|
|
131
|
-
|
|
132
|
-
```rust
|
|
133
|
-
use thiserror::Error;
|
|
134
|
-
|
|
135
|
-
#[derive(Error, Debug)]
|
|
136
|
-
enum MyError {
|
|
137
|
-
// #[from] = implements From + sets source
|
|
138
|
-
#[error("IO error")]
|
|
139
|
-
Io(#[from] std::io::Error),
|
|
140
|
-
|
|
141
|
-
// #[source] = only sets source (no From impl)
|
|
142
|
-
#[error("Parse error in file '{path}'")]
|
|
143
|
-
Parse {
|
|
144
|
-
path: String,
|
|
145
|
-
#[source]
|
|
146
|
-
source: serde_json::Error,
|
|
147
|
-
},
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## See Also
|
|
152
|
-
|
|
153
|
-
- [err-thiserror-lib](./err-thiserror-lib.md) - thiserror for error definitions
|
|
154
|
-
- [err-context-chain](./err-context-chain.md) - Adding context to errors
|
|
155
|
-
- [err-from-impl](./err-from-impl.md) - From implementations for ?
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
# err-thiserror-lib
|
|
2
|
-
|
|
3
|
-
> Use `thiserror` for library error types
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Libraries should expose typed, matchable errors so users can handle specific error conditions. `thiserror` generates `Error` trait implementations with minimal boilerplate, creating ergonomic error types that are easy to match against.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// String errors - not matchable
|
|
13
|
-
fn parse(input: &str) -> Result<Data, String> {
|
|
14
|
-
Err("parse error".to_string())
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Box<dyn Error> - not matchable
|
|
18
|
-
fn load(path: &Path) -> Result<Data, Box<dyn std::error::Error>> {
|
|
19
|
-
Err(Box::new(std::io::Error::new(std::io::ErrorKind::NotFound, "file not found")))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Manual implementation - verbose
|
|
23
|
-
#[derive(Debug)]
|
|
24
|
-
enum MyError {
|
|
25
|
-
Io(std::io::Error),
|
|
26
|
-
Parse(String),
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
impl std::fmt::Display for MyError {
|
|
30
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
31
|
-
match self {
|
|
32
|
-
MyError::Io(e) => write!(f, "io error: {}", e),
|
|
33
|
-
MyError::Parse(s) => write!(f, "parse error: {}", s),
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
impl std::error::Error for MyError {
|
|
39
|
-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
40
|
-
match self {
|
|
41
|
-
MyError::Io(e) => Some(e),
|
|
42
|
-
MyError::Parse(_) => None,
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Good
|
|
49
|
-
|
|
50
|
-
```rust
|
|
51
|
-
use thiserror::Error;
|
|
52
|
-
|
|
53
|
-
#[derive(Error, Debug)]
|
|
54
|
-
pub enum ParseError {
|
|
55
|
-
#[error("invalid syntax at line {line}: {message}")]
|
|
56
|
-
Syntax { line: usize, message: String },
|
|
57
|
-
|
|
58
|
-
#[error("unexpected end of file")]
|
|
59
|
-
UnexpectedEof,
|
|
60
|
-
|
|
61
|
-
#[error("invalid utf-8 encoding")]
|
|
62
|
-
Utf8(#[from] std::str::Utf8Error),
|
|
63
|
-
|
|
64
|
-
#[error("io error reading input")]
|
|
65
|
-
Io(#[from] std::io::Error),
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Usage
|
|
69
|
-
fn parse(input: &str) -> Result<Ast, ParseError> {
|
|
70
|
-
if input.is_empty() {
|
|
71
|
-
return Err(ParseError::UnexpectedEof);
|
|
72
|
-
}
|
|
73
|
-
// ...
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Users can match specific errors
|
|
77
|
-
match parse(input) {
|
|
78
|
-
Ok(ast) => process(ast),
|
|
79
|
-
Err(ParseError::Syntax { line, message }) => {
|
|
80
|
-
eprintln!("Syntax error on line {}: {}", line, message);
|
|
81
|
-
}
|
|
82
|
-
Err(ParseError::UnexpectedEof) => {
|
|
83
|
-
eprintln!("File ended unexpectedly");
|
|
84
|
-
}
|
|
85
|
-
Err(e) => eprintln!("Error: {}", e),
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Key Attributes
|
|
90
|
-
|
|
91
|
-
```rust
|
|
92
|
-
use thiserror::Error;
|
|
93
|
-
|
|
94
|
-
#[derive(Error, Debug)]
|
|
95
|
-
pub enum MyError {
|
|
96
|
-
// Simple message
|
|
97
|
-
#[error("operation failed")]
|
|
98
|
-
Failed,
|
|
99
|
-
|
|
100
|
-
// Interpolated fields
|
|
101
|
-
#[error("invalid value: {0}")]
|
|
102
|
-
InvalidValue(String),
|
|
103
|
-
|
|
104
|
-
// Named fields
|
|
105
|
-
#[error("connection to {host}:{port} failed")]
|
|
106
|
-
Connection { host: String, port: u16 },
|
|
107
|
-
|
|
108
|
-
// Automatic From impl with #[from]
|
|
109
|
-
#[error("database error")]
|
|
110
|
-
Database(#[from] sqlx::Error),
|
|
111
|
-
|
|
112
|
-
// Source without From (manual conversion needed)
|
|
113
|
-
#[error("validation failed")]
|
|
114
|
-
Validation {
|
|
115
|
-
#[source]
|
|
116
|
-
cause: ValidationError,
|
|
117
|
-
field: String,
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
// Transparent - delegates Display and source to inner
|
|
121
|
-
#[error(transparent)]
|
|
122
|
-
Other(#[from] anyhow::Error),
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Error Chaining
|
|
127
|
-
|
|
128
|
-
```rust
|
|
129
|
-
use thiserror::Error;
|
|
130
|
-
|
|
131
|
-
#[derive(Error, Debug)]
|
|
132
|
-
pub enum ConfigError {
|
|
133
|
-
#[error("failed to read config file")]
|
|
134
|
-
Read(#[source] std::io::Error),
|
|
135
|
-
|
|
136
|
-
#[error("failed to parse config")]
|
|
137
|
-
Parse(#[source] toml::de::Error),
|
|
138
|
-
|
|
139
|
-
#[error("invalid config value for '{key}'")]
|
|
140
|
-
InvalidValue {
|
|
141
|
-
key: String,
|
|
142
|
-
#[source]
|
|
143
|
-
cause: ValueError,
|
|
144
|
-
},
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Error chain is preserved
|
|
148
|
-
fn load_config(path: &Path) -> Result<Config, ConfigError> {
|
|
149
|
-
let content = std::fs::read_to_string(path)
|
|
150
|
-
.map_err(ConfigError::Read)?;
|
|
151
|
-
|
|
152
|
-
let config: Config = toml::from_str(&content)
|
|
153
|
-
.map_err(ConfigError::Parse)?;
|
|
154
|
-
|
|
155
|
-
Ok(config)
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Library vs Application
|
|
160
|
-
|
|
161
|
-
| Context | Crate | Why |
|
|
162
|
-
|---------|-------|-----|
|
|
163
|
-
| Library | `thiserror` | Typed errors users can match |
|
|
164
|
-
| Application | `anyhow` | Easy error handling with context |
|
|
165
|
-
| Both | `thiserror` for public API, `anyhow` internally | Best of both |
|
|
166
|
-
|
|
167
|
-
## See Also
|
|
168
|
-
|
|
169
|
-
- [err-anyhow-app](err-anyhow-app.md) - Use anyhow for applications
|
|
170
|
-
- [err-from-impl](err-from-impl.md) - Use #[from] for automatic conversion
|
|
171
|
-
- [err-source-chain](err-source-chain.md) - Use #[source] to chain errors
|