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,124 +0,0 @@
|
|
|
1
|
-
# own-copy-small
|
|
2
|
-
|
|
3
|
-
> Implement `Copy` for small, simple types
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Types that implement `Copy` are implicitly duplicated on assignment instead of moved. This eliminates the need for explicit `.clone()` calls and makes the code more ergonomic. For small types (generally ≤16 bytes), copying is as fast or faster than moving a pointer.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Small type without Copy - requires explicit clone
|
|
13
|
-
#[derive(Clone, Debug)]
|
|
14
|
-
struct Point {
|
|
15
|
-
x: f64,
|
|
16
|
-
y: f64,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
fn distance(p1: Point, p2: Point) -> f64 {
|
|
20
|
-
((p2.x - p1.x).powi(2) + (p2.y - p1.y).powi(2)).sqrt()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let origin = Point { x: 0.0, y: 0.0 };
|
|
24
|
-
let target = Point { x: 3.0, y: 4.0 };
|
|
25
|
-
|
|
26
|
-
let d1 = distance(origin.clone(), target.clone()); // Tedious
|
|
27
|
-
let d2 = distance(origin.clone(), target.clone()); // Every use needs clone
|
|
28
|
-
// origin and target still usable but verbose
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
// Small type with Copy - implicit duplication
|
|
35
|
-
#[derive(Clone, Copy, Debug)]
|
|
36
|
-
struct Point {
|
|
37
|
-
x: f64,
|
|
38
|
-
y: f64,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
fn distance(p1: Point, p2: Point) -> f64 {
|
|
42
|
-
((p2.x - p1.x).powi(2) + (p2.y - p1.y).powi(2)).sqrt()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let origin = Point { x: 0.0, y: 0.0 };
|
|
46
|
-
let target = Point { x: 3.0, y: 4.0 };
|
|
47
|
-
|
|
48
|
-
let d1 = distance(origin, target); // Implicitly copied
|
|
49
|
-
let d2 = distance(origin, target); // Still works!
|
|
50
|
-
// origin and target remain valid
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Copy Requirements
|
|
54
|
-
|
|
55
|
-
A type can implement `Copy` only if:
|
|
56
|
-
1. All fields implement `Copy`
|
|
57
|
-
2. No custom `Drop` implementation
|
|
58
|
-
3. No heap-allocated data (`String`, `Vec`, `Box`, etc.)
|
|
59
|
-
|
|
60
|
-
```rust
|
|
61
|
-
// ✅ Can be Copy
|
|
62
|
-
#[derive(Clone, Copy)]
|
|
63
|
-
struct Color {
|
|
64
|
-
r: u8,
|
|
65
|
-
g: u8,
|
|
66
|
-
b: u8,
|
|
67
|
-
a: u8,
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ❌ Cannot be Copy - contains String
|
|
71
|
-
#[derive(Clone)]
|
|
72
|
-
struct Person {
|
|
73
|
-
name: String, // String is not Copy
|
|
74
|
-
age: u32,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ❌ Cannot be Copy - has Drop
|
|
78
|
-
struct FileHandle {
|
|
79
|
-
fd: i32,
|
|
80
|
-
}
|
|
81
|
-
impl Drop for FileHandle {
|
|
82
|
-
fn drop(&mut self) { /* close file */ }
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Size Guidelines
|
|
87
|
-
|
|
88
|
-
| Size | Recommendation |
|
|
89
|
-
|------|----------------|
|
|
90
|
-
| ≤ 16 bytes | Implement `Copy` |
|
|
91
|
-
| 17-64 bytes | Consider `Copy`, benchmark if critical |
|
|
92
|
-
| > 64 bytes | Probably don't, prefer references |
|
|
93
|
-
|
|
94
|
-
```rust
|
|
95
|
-
use std::mem::size_of;
|
|
96
|
-
|
|
97
|
-
#[derive(Clone, Copy)]
|
|
98
|
-
struct SmallId(u64); // 8 bytes ✅
|
|
99
|
-
|
|
100
|
-
#[derive(Clone, Copy)]
|
|
101
|
-
struct Rect { x: f32, y: f32, w: f32, h: f32 } // 16 bytes ✅
|
|
102
|
-
|
|
103
|
-
#[derive(Clone)] // No Copy - 72 bytes
|
|
104
|
-
struct Transform {
|
|
105
|
-
matrix: [[f64; 3]; 3], // 72 bytes, too large
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Common Copy Types
|
|
110
|
-
|
|
111
|
-
Standard library types that are `Copy`:
|
|
112
|
-
- All primitives: `i32`, `f64`, `bool`, `char`, etc.
|
|
113
|
-
- References: `&T`, `&mut T`
|
|
114
|
-
- Raw pointers: `*const T`, `*mut T`
|
|
115
|
-
- Function pointers: `fn(T) -> U`
|
|
116
|
-
- Tuples of `Copy` types: `(i32, f64)`
|
|
117
|
-
- Arrays of `Copy` types: `[u8; 32]`
|
|
118
|
-
- `Option<T>` where `T: Copy`
|
|
119
|
-
- `PhantomData<T>`
|
|
120
|
-
|
|
121
|
-
## See Also
|
|
122
|
-
|
|
123
|
-
- [own-clone-explicit](./own-clone-explicit.md) - When Clone without Copy is appropriate
|
|
124
|
-
- [type-newtype-ids](./type-newtype-ids.md) - Newtype pattern often uses Copy
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# own-cow-conditional
|
|
2
|
-
|
|
3
|
-
> Use `Cow<'a, T>` for conditional ownership
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`Cow` (Clone-on-Write) lets you avoid allocations when you *might* need to own data but usually don't. It holds either a borrowed reference or an owned value, cloning only when mutation is needed.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Always allocates, even when input doesn't need modification
|
|
13
|
-
fn normalize_path(path: &str) -> String {
|
|
14
|
-
if path.contains("//") {
|
|
15
|
-
path.replace("//", "/") // Allocation needed
|
|
16
|
-
} else {
|
|
17
|
-
path.to_string() // Unnecessary allocation!
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Always clones the error message
|
|
22
|
-
fn format_error(code: u32) -> String {
|
|
23
|
-
match code {
|
|
24
|
-
404 => "Not Found".to_string(), // Unnecessary!
|
|
25
|
-
500 => "Internal Error".to_string(), // Unnecessary!
|
|
26
|
-
_ => format!("Error {}", code), // This one needs allocation
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
use std::borrow::Cow;
|
|
35
|
-
|
|
36
|
-
// Only allocates when needed
|
|
37
|
-
fn normalize_path(path: &str) -> Cow<'_, str> {
|
|
38
|
-
if path.contains("//") {
|
|
39
|
-
Cow::Owned(path.replace("//", "/")) // Allocate
|
|
40
|
-
} else {
|
|
41
|
-
Cow::Borrowed(path) // Zero-cost borrow
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Static strings stay borrowed
|
|
46
|
-
fn format_error(code: u32) -> Cow<'static, str> {
|
|
47
|
-
match code {
|
|
48
|
-
404 => Cow::Borrowed("Not Found"), // No allocation
|
|
49
|
-
500 => Cow::Borrowed("Internal Error"), // No allocation
|
|
50
|
-
_ => Cow::Owned(format!("Error {}", code)), // Allocate only for unknown
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Real-World Example from ripgrep
|
|
56
|
-
|
|
57
|
-
```rust
|
|
58
|
-
// https://github.com/BurntSushi/ripgrep/blob/master/crates/globset/src/pathutil.rs
|
|
59
|
-
pub(crate) fn file_name<'a>(path: &Cow<'a, [u8]>) -> Option<Cow<'a, [u8]>> {
|
|
60
|
-
let last_slash = path.rfind_byte(b'/').map(|i| i + 1).unwrap_or(0);
|
|
61
|
-
match *path {
|
|
62
|
-
Cow::Borrowed(path) => Some(Cow::Borrowed(&path[last_slash..])),
|
|
63
|
-
Cow::Owned(ref path) => {
|
|
64
|
-
let mut path = path.clone();
|
|
65
|
-
path.drain_bytes(..last_slash);
|
|
66
|
-
Some(Cow::Owned(path))
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Clone-on-Write Pattern
|
|
73
|
-
|
|
74
|
-
```rust
|
|
75
|
-
use std::borrow::Cow;
|
|
76
|
-
|
|
77
|
-
fn process_text(text: Cow<'_, str>) -> Cow<'_, str> {
|
|
78
|
-
if text.contains("bad_word") {
|
|
79
|
-
// to_mut() clones if borrowed, returns &mut if owned
|
|
80
|
-
let mut owned = text.into_owned();
|
|
81
|
-
owned = owned.replace("bad_word", "***");
|
|
82
|
-
Cow::Owned(owned)
|
|
83
|
-
} else {
|
|
84
|
-
text // Pass through unchanged
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Usage
|
|
89
|
-
let borrowed: Cow<str> = Cow::Borrowed("hello world");
|
|
90
|
-
let result = process_text(borrowed); // No allocation!
|
|
91
|
-
|
|
92
|
-
let with_bad: Cow<str> = Cow::Borrowed("hello bad_word");
|
|
93
|
-
let result = process_text(with_bad); // Allocates only here
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Cow with Collections
|
|
97
|
-
|
|
98
|
-
```rust
|
|
99
|
-
use std::borrow::Cow;
|
|
100
|
-
|
|
101
|
-
// Mixed borrowed/owned in a collection
|
|
102
|
-
fn collect_errors<'a>(
|
|
103
|
-
static_errors: &[&'static str],
|
|
104
|
-
dynamic_errors: Vec<String>,
|
|
105
|
-
) -> Vec<Cow<'a, str>> {
|
|
106
|
-
let mut errors: Vec<Cow<str>> = Vec::new();
|
|
107
|
-
|
|
108
|
-
// Static strings - no allocation
|
|
109
|
-
for &e in static_errors {
|
|
110
|
-
errors.push(Cow::Borrowed(e));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Dynamic strings - take ownership
|
|
114
|
-
for e in dynamic_errors {
|
|
115
|
-
errors.push(Cow::Owned(e));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
errors
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## When to Use Cow
|
|
123
|
-
|
|
124
|
-
| Situation | Use Cow? |
|
|
125
|
-
|-----------|----------|
|
|
126
|
-
| Usually borrow, sometimes own | Yes |
|
|
127
|
-
| Always need owned data | No, just use owned type |
|
|
128
|
-
| Always borrow | No, just use reference |
|
|
129
|
-
| Hot path, avoiding all allocations | Yes |
|
|
130
|
-
| Returning static strings or formatted | Yes |
|
|
131
|
-
|
|
132
|
-
## See Also
|
|
133
|
-
|
|
134
|
-
- [own-borrow-over-clone](own-borrow-over-clone.md) - Prefer borrowing over cloning
|
|
135
|
-
- [mem-avoid-format](mem-avoid-format.md) - Avoid format! when possible
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# own-lifetime-elision
|
|
2
|
-
|
|
3
|
-
> Rely on lifetime elision rules; add explicit lifetimes only when required
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Rust's lifetime elision rules handle most common borrowing patterns automatically. Adding explicit lifetimes where they're not needed clutters code without adding clarity. However, understanding when elision applies helps you know when explicit lifetimes are truly necessary.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Unnecessary explicit lifetimes - elision handles these
|
|
13
|
-
fn first_word<'a>(s: &'a str) -> &'a str {
|
|
14
|
-
s.split_whitespace().next().unwrap_or("")
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fn get_name<'a>(person: &'a Person) -> &'a str {
|
|
18
|
-
&person.name
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
impl<'a> Display for Wrapper<'a> {
|
|
22
|
-
fn fmt<'b>(&'b self, f: &'b mut Formatter<'_>) -> fmt::Result {
|
|
23
|
-
write!(f, "{}", self.0)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Good
|
|
29
|
-
|
|
30
|
-
```rust
|
|
31
|
-
// Let elision do its job
|
|
32
|
-
fn first_word(s: &str) -> &str {
|
|
33
|
-
s.split_whitespace().next().unwrap_or("")
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
fn get_name(person: &Person) -> &str {
|
|
37
|
-
&person.name
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
impl Display for Wrapper<'_> {
|
|
41
|
-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
42
|
-
write!(f, "{}", self.0)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## The Three Elision Rules
|
|
48
|
-
|
|
49
|
-
1. **Each input reference gets its own lifetime:**
|
|
50
|
-
```rust
|
|
51
|
-
fn foo(x: &str, y: &str)
|
|
52
|
-
// becomes
|
|
53
|
-
fn foo<'a, 'b>(x: &'a str, y: &'b str)
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
2. **One input reference → output gets same lifetime:**
|
|
57
|
-
```rust
|
|
58
|
-
fn foo(x: &str) -> &str
|
|
59
|
-
// becomes
|
|
60
|
-
fn foo<'a>(x: &'a str) -> &'a str
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
3. **Method with `&self`/`&mut self` → output gets self's lifetime:**
|
|
64
|
-
```rust
|
|
65
|
-
fn foo(&self, x: &str) -> &str
|
|
66
|
-
// becomes
|
|
67
|
-
fn foo<'a, 'b>(&'a self, x: &'b str) -> &'a str
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## When Explicit Lifetimes ARE Required
|
|
71
|
-
|
|
72
|
-
```rust
|
|
73
|
-
// Multiple input references, output could come from either
|
|
74
|
-
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
|
75
|
-
if x.len() > y.len() { x } else { y }
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Struct holding references
|
|
79
|
-
struct Parser<'input> {
|
|
80
|
-
source: &'input str,
|
|
81
|
-
position: usize,
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Multiple distinct lifetimes needed
|
|
85
|
-
struct Context<'s, 'c> {
|
|
86
|
-
source: &'s str,
|
|
87
|
-
cache: &'c mut Cache,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Static lifetime for constants
|
|
91
|
-
fn get_default() -> &'static str {
|
|
92
|
-
"default"
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Anonymous Lifetime `'_`
|
|
97
|
-
|
|
98
|
-
Use `'_` to let the compiler infer while being explicit about the presence of a lifetime:
|
|
99
|
-
|
|
100
|
-
```rust
|
|
101
|
-
// In struct definitions
|
|
102
|
-
impl Iterator for Parser<'_> {
|
|
103
|
-
type Item = Token;
|
|
104
|
-
fn next(&mut self) -> Option<Self::Item> { ... }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// In function signatures where it adds clarity
|
|
108
|
-
fn parse(input: &str) -> Result<Ast<'_>, Error> { ... }
|
|
109
|
-
|
|
110
|
-
// Especially useful in trait bounds
|
|
111
|
-
fn process(data: &impl AsRef<str>) -> Cow<'_, str> { ... }
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Common Patterns
|
|
115
|
-
|
|
116
|
-
```rust
|
|
117
|
-
// ✅ Elision works
|
|
118
|
-
fn trim(s: &str) -> &str { s.trim() }
|
|
119
|
-
fn first(v: &[i32]) -> Option<&i32> { v.first() }
|
|
120
|
-
fn name(&self) -> &str { &self.name }
|
|
121
|
-
|
|
122
|
-
// ❌ Elision fails - multiple inputs, ambiguous output
|
|
123
|
-
fn pick(a: &str, b: &str, first: bool) -> &str // Error!
|
|
124
|
-
|
|
125
|
-
// ✅ Fixed with explicit lifetime
|
|
126
|
-
fn pick<'a>(a: &'a str, b: &'a str, first: bool) -> &'a str {
|
|
127
|
-
if first { a } else { b }
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## See Also
|
|
132
|
-
|
|
133
|
-
- [own-borrow-over-clone](./own-borrow-over-clone.md) - Prefer borrowing to avoid ownership issues
|
|
134
|
-
- [api-impl-asref](./api-impl-asref.md) - Generic borrowing with AsRef
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# own-move-large
|
|
2
|
-
|
|
3
|
-
> Move large types instead of copying; use `Box` if moves are expensive
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
In Rust, "moving" a value means copying its bytes to a new location and invalidating the old one. For large types (hundreds of bytes), this memcpy can be expensive. Boxing large types reduces move cost to copying a single pointer (8 bytes), making moves cheap regardless of the actual data size.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Large struct moved repeatedly = expensive memcpy each time
|
|
13
|
-
struct GameState {
|
|
14
|
-
board: [[Cell; 100]; 100], // 10,000 cells
|
|
15
|
-
history: [Move; 1000], // 1,000 moves
|
|
16
|
-
players: [Player; 4], // Player data
|
|
17
|
-
// Total: potentially tens of KB
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
fn process_state(state: GameState) -> GameState {
|
|
21
|
-
// Moving ~40KB+ of data
|
|
22
|
-
let mut new_state = state; // Memcpy here
|
|
23
|
-
new_state.apply_rules();
|
|
24
|
-
new_state // Memcpy on return
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let state = GameState::new();
|
|
28
|
-
let state = process_state(state); // Two large memcpys
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Good
|
|
32
|
-
|
|
33
|
-
```rust
|
|
34
|
-
// Box reduces move cost to 8 bytes
|
|
35
|
-
struct GameState {
|
|
36
|
-
board: Box<[[Cell; 100]; 100]>, // Pointer to heap
|
|
37
|
-
history: Vec<Move>, // Already heap-allocated
|
|
38
|
-
players: [Player; 4],
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
fn process_state(mut state: GameState) -> GameState {
|
|
42
|
-
// Moving just pointers + small inline data
|
|
43
|
-
state.apply_rules();
|
|
44
|
-
state // Cheap move
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Or use Box at call site for one-off cases
|
|
48
|
-
fn process_large(state: Box<LargeStruct>) -> Box<LargeStruct> {
|
|
49
|
-
// 8-byte move regardless of LargeStruct size
|
|
50
|
-
state
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## When to Box
|
|
55
|
-
|
|
56
|
-
| Type Size | Move Frequency | Recommendation |
|
|
57
|
-
|-----------|----------------|----------------|
|
|
58
|
-
| < 128 bytes | Any | Don't box |
|
|
59
|
-
| 128-512 bytes | Rare | Probably don't box |
|
|
60
|
-
| 128-512 bytes | Frequent | Consider boxing |
|
|
61
|
-
| > 512 bytes | Any | Box or use references |
|
|
62
|
-
| > 4KB | Any | Definitely box |
|
|
63
|
-
|
|
64
|
-
## Stack vs Heap Tradeoffs
|
|
65
|
-
|
|
66
|
-
```rust
|
|
67
|
-
// Stack: fast allocation, limited size, moves copy bytes
|
|
68
|
-
struct StackHeavy {
|
|
69
|
-
data: [u8; 4096], // 4KB on stack
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Heap: allocation cost, unlimited size, moves copy pointer
|
|
73
|
-
struct HeapLight {
|
|
74
|
-
data: Box<[u8; 4096]>, // 8 bytes on stack, 4KB on heap
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Measure with size_of
|
|
78
|
-
use std::mem::size_of;
|
|
79
|
-
assert_eq!(size_of::<StackHeavy>(), 4096);
|
|
80
|
-
assert_eq!(size_of::<HeapLight>(), 8);
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Alternative: References
|
|
84
|
-
|
|
85
|
-
When you don't need ownership transfer, use references:
|
|
86
|
-
|
|
87
|
-
```rust
|
|
88
|
-
// Best: no move at all
|
|
89
|
-
fn analyze_state(state: &GameState) -> Analysis {
|
|
90
|
-
// Borrows state, no copying
|
|
91
|
-
compute_analysis(state)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Mutable borrow for in-place modification
|
|
95
|
-
fn update_state(state: &mut GameState) {
|
|
96
|
-
state.tick();
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Pattern: Builder Returns Boxed
|
|
101
|
-
|
|
102
|
-
```rust
|
|
103
|
-
impl LargeConfig {
|
|
104
|
-
pub fn builder() -> ConfigBuilder {
|
|
105
|
-
ConfigBuilder::default()
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
impl ConfigBuilder {
|
|
110
|
-
// Return boxed to avoid large move
|
|
111
|
-
pub fn build(self) -> Box<LargeConfig> {
|
|
112
|
-
Box::new(LargeConfig {
|
|
113
|
-
// ... fields from builder
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Profile First
|
|
120
|
-
|
|
121
|
-
Don't prematurely optimize. Use tools to identify if moves are actually a bottleneck:
|
|
122
|
-
|
|
123
|
-
```rust
|
|
124
|
-
// Check type sizes
|
|
125
|
-
println!("Size of GameState: {}", std::mem::size_of::<GameState>());
|
|
126
|
-
|
|
127
|
-
// Profile with cargo flamegraph or perf to find hot memcpys
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## See Also
|
|
131
|
-
|
|
132
|
-
- [own-copy-small](./own-copy-small.md) - Cheap types should be Copy
|
|
133
|
-
- [mem-box-large-variant](./mem-box-large-variant.md) - Boxing enum variants
|
|
134
|
-
- [perf-profile-first](./perf-profile-first.md) - Measure before optimizing
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# own-mutex-interior
|
|
2
|
-
|
|
3
|
-
> Use `Mutex<T>` for interior mutability across threads
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
When you need shared mutable state across threads, `Mutex<T>` provides safe interior mutability with synchronization. Unlike `RefCell`, `Mutex` is `Send + Sync` and uses OS-level locking to ensure only one thread can access the data at a time.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
use std::cell::RefCell;
|
|
13
|
-
use std::sync::Arc;
|
|
14
|
-
|
|
15
|
-
// RefCell is !Sync - this won't compile
|
|
16
|
-
let shared = Arc::new(RefCell::new(vec![]));
|
|
17
|
-
|
|
18
|
-
// ERROR: RefCell cannot be shared between threads safely
|
|
19
|
-
std::thread::spawn({
|
|
20
|
-
let shared = shared.clone();
|
|
21
|
-
move || shared.borrow_mut().push(1)
|
|
22
|
-
});
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Good
|
|
26
|
-
|
|
27
|
-
```rust
|
|
28
|
-
use std::sync::{Arc, Mutex};
|
|
29
|
-
|
|
30
|
-
let shared = Arc::new(Mutex::new(vec![]));
|
|
31
|
-
|
|
32
|
-
let handles: Vec<_> = (0..10).map(|i| {
|
|
33
|
-
let shared = shared.clone();
|
|
34
|
-
std::thread::spawn(move || {
|
|
35
|
-
let mut data = shared.lock().unwrap();
|
|
36
|
-
data.push(i);
|
|
37
|
-
})
|
|
38
|
-
}).collect();
|
|
39
|
-
|
|
40
|
-
for handle in handles {
|
|
41
|
-
handle.join().unwrap();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
println!("{:?}", shared.lock().unwrap()); // All values present
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Mutex Poisoning
|
|
48
|
-
|
|
49
|
-
If a thread panics while holding a lock, the mutex becomes "poisoned":
|
|
50
|
-
|
|
51
|
-
```rust
|
|
52
|
-
use std::sync::{Arc, Mutex};
|
|
53
|
-
|
|
54
|
-
let mutex = Arc::new(Mutex::new(0));
|
|
55
|
-
|
|
56
|
-
// Handle poisoning gracefully
|
|
57
|
-
match mutex.lock() {
|
|
58
|
-
Ok(guard) => println!("Value: {}", *guard),
|
|
59
|
-
Err(poisoned) => {
|
|
60
|
-
// Recover the data anyway
|
|
61
|
-
let guard = poisoned.into_inner();
|
|
62
|
-
println!("Recovered value: {}", *guard);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Or ignore poisoning (use with caution)
|
|
67
|
-
let guard = mutex.lock().unwrap_or_else(|e| e.into_inner());
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Prefer parking_lot::Mutex
|
|
71
|
-
|
|
72
|
-
For better performance, consider `parking_lot::Mutex`:
|
|
73
|
-
|
|
74
|
-
```rust
|
|
75
|
-
use parking_lot::Mutex;
|
|
76
|
-
use std::sync::Arc;
|
|
77
|
-
|
|
78
|
-
let shared = Arc::new(Mutex::new(vec![]));
|
|
79
|
-
|
|
80
|
-
// No poisoning, no Result to unwrap
|
|
81
|
-
let mut data = shared.lock();
|
|
82
|
-
data.push(42);
|
|
83
|
-
// Lock automatically released when guard drops
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Benefits of `parking_lot`:
|
|
87
|
-
- No poisoning (returns guard directly)
|
|
88
|
-
- Smaller size (1 byte vs 40+ bytes)
|
|
89
|
-
- Better performance under contention
|
|
90
|
-
- Fair locking option available
|
|
91
|
-
|
|
92
|
-
## When to Use What
|
|
93
|
-
|
|
94
|
-
| Type | Threading | Overhead | Use Case |
|
|
95
|
-
|------|-----------|----------|----------|
|
|
96
|
-
| `RefCell<T>` | Single | Minimal | Interior mutability, same thread |
|
|
97
|
-
| `Mutex<T>` | Multi | Locking | Shared mutable state across threads |
|
|
98
|
-
| `RwLock<T>` | Multi | Locking | Many readers, few writers |
|
|
99
|
-
| `parking_lot::Mutex` | Multi | Less | Drop-in std::Mutex replacement |
|
|
100
|
-
|
|
101
|
-
## See Also
|
|
102
|
-
|
|
103
|
-
- [own-rwlock-readers](./own-rwlock-readers.md) - When reads dominate writes
|
|
104
|
-
- [own-refcell-interior](./own-refcell-interior.md) - Single-threaded alternative
|
|
105
|
-
- [async-no-lock-await](./async-no-lock-await.md) - Avoiding locks across await points
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# own-rc-single-thread
|
|
2
|
-
|
|
3
|
-
> Use `Rc<T>` for shared ownership in single-threaded contexts
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
`Rc<T>` (Reference Counted) provides shared ownership without the atomic overhead of `Arc<T>`. In single-threaded code, `Rc` is faster because it uses non-atomic reference counting. Using `Arc` when you don't need thread-safety wastes CPU cycles on unnecessary synchronization.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
use std::sync::Arc;
|
|
13
|
-
|
|
14
|
-
// Single-threaded application using Arc unnecessarily
|
|
15
|
-
fn build_tree() -> Arc<Node> {
|
|
16
|
-
let root = Arc::new(Node::new("root"));
|
|
17
|
-
let child1 = Arc::new(Node::new("child1"));
|
|
18
|
-
let child2 = Arc::new(Node::new("child2"));
|
|
19
|
-
|
|
20
|
-
// All in same thread, but paying atomic overhead
|
|
21
|
-
root.add_child(child1.clone());
|
|
22
|
-
root.add_child(child2.clone());
|
|
23
|
-
root
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Atomic operations have measurable overhead even without contention.
|
|
28
|
-
|
|
29
|
-
## Good
|
|
30
|
-
|
|
31
|
-
```rust
|
|
32
|
-
use std::rc::Rc;
|
|
33
|
-
|
|
34
|
-
// Single-threaded: use Rc for zero atomic overhead
|
|
35
|
-
fn build_tree() -> Rc<Node> {
|
|
36
|
-
let root = Rc::new(Node::new("root"));
|
|
37
|
-
let child1 = Rc::new(Node::new("child1"));
|
|
38
|
-
let child2 = Rc::new(Node::new("child2"));
|
|
39
|
-
|
|
40
|
-
root.add_child(child1.clone());
|
|
41
|
-
root.add_child(child2.clone());
|
|
42
|
-
root
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Compiler enforces single-thread: Rc is !Send + !Sync
|
|
46
|
-
// Attempting to send across threads = compile error
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Decision Guide
|
|
50
|
-
|
|
51
|
-
| Scenario | Use |
|
|
52
|
-
|----------|-----|
|
|
53
|
-
| Single-threaded, shared ownership | `Rc<T>` |
|
|
54
|
-
| Multi-threaded, shared ownership | `Arc<T>` |
|
|
55
|
-
| Single owner, might need multiple later | Start with `Rc`, upgrade if needed |
|
|
56
|
-
| Library code, unknown threading model | `Arc<T>` (safer default) |
|
|
57
|
-
|
|
58
|
-
## Evidence
|
|
59
|
-
|
|
60
|
-
The Rust standard library itself uses `Rc` extensively in single-threaded contexts like the `std::rc` module documentation examples.
|
|
61
|
-
|
|
62
|
-
## See Also
|
|
63
|
-
|
|
64
|
-
- [own-arc-shared](./own-arc-shared.md) - When you need thread-safe sharing
|
|
65
|
-
- [own-refcell-interior](./own-refcell-interior.md) - Combining Rc with interior mutability
|