agy-superpowers 5.1.4 → 5.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/agent/rules/debug-confirmation-policy.md +34 -0
- package/template/agent/rules/language-matching.md +32 -0
- package/template/agent/skills/rust-developer/SKILL.md +281 -0
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# perf-chain-avoid
|
|
2
|
+
|
|
3
|
+
> Avoid chain in hot loops
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`Iterator::chain()` adds overhead for checking which iterator is active on every `.next()` call. In hot loops, this branch prediction overhead can impact performance. For performance-critical code, prefer single iterators or pre-combined collections.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Chain in hot inner loop
|
|
13
|
+
fn process_hot_path(a: &[i32], b: &[i32]) -> i64 {
|
|
14
|
+
let mut sum = 0i64;
|
|
15
|
+
|
|
16
|
+
// Called millions of times
|
|
17
|
+
for _ in 0..1_000_000 {
|
|
18
|
+
for x in a.iter().chain(b.iter()) { // Branch every iteration
|
|
19
|
+
sum += *x as i64;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
sum
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Chaining multiple small slices in tight loop
|
|
26
|
+
fn combine_results(parts: &[&[u8]]) -> Vec<u8> {
|
|
27
|
+
let mut result = Vec::new();
|
|
28
|
+
for part in parts {
|
|
29
|
+
for byte in std::iter::once(&0u8).chain(part.iter()) {
|
|
30
|
+
result.push(*byte);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
result
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Good
|
|
38
|
+
|
|
39
|
+
```rust
|
|
40
|
+
// Separate loops - branch-free inner loops
|
|
41
|
+
fn process_hot_path(a: &[i32], b: &[i32]) -> i64 {
|
|
42
|
+
let mut sum = 0i64;
|
|
43
|
+
|
|
44
|
+
for _ in 0..1_000_000 {
|
|
45
|
+
for x in a {
|
|
46
|
+
sum += *x as i64;
|
|
47
|
+
}
|
|
48
|
+
for x in b {
|
|
49
|
+
sum += *x as i64;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
sum
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Pre-combine outside hot loop
|
|
56
|
+
fn combine_results(parts: &[&[u8]]) -> Vec<u8> {
|
|
57
|
+
let mut result = Vec::new();
|
|
58
|
+
for part in parts {
|
|
59
|
+
result.push(0u8);
|
|
60
|
+
result.extend_from_slice(part);
|
|
61
|
+
}
|
|
62
|
+
result
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## When Chain Is Fine
|
|
67
|
+
|
|
68
|
+
Chain is perfectly acceptable when:
|
|
69
|
+
|
|
70
|
+
```rust
|
|
71
|
+
// One-time iteration, not in hot path
|
|
72
|
+
fn collect_all(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
|
|
73
|
+
a.into_iter().chain(b).collect()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Lazy evaluation with short-circuit
|
|
77
|
+
fn find_in_either(a: &[Item], b: &[Item], target: i32) -> Option<&Item> {
|
|
78
|
+
a.iter().chain(b.iter()).find(|x| x.id == target)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Small number of elements
|
|
82
|
+
fn get_prefixes() -> impl Iterator<Item = &'static str> {
|
|
83
|
+
["Mr.", "Mrs.", "Dr."].iter().copied()
|
|
84
|
+
.chain(["Prof."].iter().copied())
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Alternative Patterns
|
|
89
|
+
|
|
90
|
+
### Pre-allocate and Extend
|
|
91
|
+
|
|
92
|
+
```rust
|
|
93
|
+
fn merge_slices(slices: &[&[i32]]) -> Vec<i32> {
|
|
94
|
+
let total: usize = slices.iter().map(|s| s.len()).sum();
|
|
95
|
+
let mut result = Vec::with_capacity(total);
|
|
96
|
+
for slice in slices {
|
|
97
|
+
result.extend_from_slice(slice);
|
|
98
|
+
}
|
|
99
|
+
result
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Use append for Vecs
|
|
104
|
+
|
|
105
|
+
```rust
|
|
106
|
+
fn combine_vecs(mut a: Vec<i32>, mut b: Vec<i32>) -> Vec<i32> {
|
|
107
|
+
a.append(&mut b); // Moves elements, no reallocation if a has capacity
|
|
108
|
+
a
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Flatten Instead of Chain
|
|
113
|
+
|
|
114
|
+
```rust
|
|
115
|
+
// Instead of: a.iter().chain(b.iter()).chain(c.iter())
|
|
116
|
+
let all = [a, b, c];
|
|
117
|
+
for item in all.iter().flat_map(|slice| slice.iter()) {
|
|
118
|
+
process(item);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Performance Impact
|
|
123
|
+
|
|
124
|
+
| Pattern | Per-Item Overhead |
|
|
125
|
+
|---------|-------------------|
|
|
126
|
+
| Single iterator | None |
|
|
127
|
+
| `chain(a, b)` | 1 branch per item |
|
|
128
|
+
| `chain(a, b, c)` | 2 branches per item |
|
|
129
|
+
| Nested chains | Compounds |
|
|
130
|
+
| Separate loops | None (but code duplication) |
|
|
131
|
+
|
|
132
|
+
## See Also
|
|
133
|
+
|
|
134
|
+
- [perf-iter-over-index](./perf-iter-over-index.md) - Prefer iterators
|
|
135
|
+
- [perf-extend-batch](./perf-extend-batch.md) - Batch insertions
|
|
136
|
+
- [opt-cache-friendly](./opt-cache-friendly.md) - Cache-friendly patterns
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# perf-collect-into
|
|
2
|
+
|
|
3
|
+
> Use collect_into for reusing containers
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`collect_into()` (stabilized in Rust 1.83) allows collecting iterator results into an existing collection, reusing its allocation. This avoids the allocation that `collect()` would make for a new collection.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Allocates new Vec each time
|
|
13
|
+
fn process_batches(batches: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
|
|
14
|
+
batches.into_iter()
|
|
15
|
+
.map(|batch| {
|
|
16
|
+
batch.into_iter()
|
|
17
|
+
.filter(|x| *x > 0)
|
|
18
|
+
.collect::<Vec<_>>() // New allocation per batch
|
|
19
|
+
})
|
|
20
|
+
.collect()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Can't reuse cleared buffer
|
|
24
|
+
fn filter_loop(data: &[Vec<i32>]) {
|
|
25
|
+
for batch in data {
|
|
26
|
+
let filtered: Vec<_> = batch.iter()
|
|
27
|
+
.filter(|&&x| x > 0)
|
|
28
|
+
.copied()
|
|
29
|
+
.collect(); // New allocation each iteration
|
|
30
|
+
process(&filtered);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Good
|
|
36
|
+
|
|
37
|
+
```rust
|
|
38
|
+
// Reuse buffer with collect_into
|
|
39
|
+
fn filter_loop(data: &[Vec<i32>]) {
|
|
40
|
+
let mut buffer = Vec::new();
|
|
41
|
+
|
|
42
|
+
for batch in data {
|
|
43
|
+
buffer.clear(); // Keep allocation
|
|
44
|
+
batch.iter()
|
|
45
|
+
.filter(|&&x| x > 0)
|
|
46
|
+
.copied()
|
|
47
|
+
.collect_into(&mut buffer);
|
|
48
|
+
process(&buffer);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Also works with extend pattern
|
|
53
|
+
fn filter_loop_extend(data: &[Vec<i32>]) {
|
|
54
|
+
let mut buffer = Vec::new();
|
|
55
|
+
|
|
56
|
+
for batch in data {
|
|
57
|
+
buffer.clear();
|
|
58
|
+
buffer.extend(
|
|
59
|
+
batch.iter()
|
|
60
|
+
.filter(|&&x| x > 0)
|
|
61
|
+
.copied()
|
|
62
|
+
);
|
|
63
|
+
process(&buffer);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Pre-1.83 Alternative: extend
|
|
69
|
+
|
|
70
|
+
Before `collect_into()` was stabilized, use `extend()`:
|
|
71
|
+
|
|
72
|
+
```rust
|
|
73
|
+
fn reuse_buffer(data: &[Vec<i32>]) {
|
|
74
|
+
let mut buffer = Vec::new();
|
|
75
|
+
|
|
76
|
+
for batch in data {
|
|
77
|
+
buffer.clear();
|
|
78
|
+
buffer.extend(batch.iter().filter(|&&x| x > 0).copied());
|
|
79
|
+
process(&buffer);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Pattern: Transform and Reuse
|
|
85
|
+
|
|
86
|
+
```rust
|
|
87
|
+
fn transform_batches(batches: &[Vec<RawData>]) -> Vec<ProcessedData> {
|
|
88
|
+
let mut temp = Vec::new();
|
|
89
|
+
let mut all_results = Vec::new();
|
|
90
|
+
|
|
91
|
+
for batch in batches {
|
|
92
|
+
temp.clear();
|
|
93
|
+
batch.iter()
|
|
94
|
+
.map(ProcessedData::from)
|
|
95
|
+
.collect_into(&mut temp);
|
|
96
|
+
|
|
97
|
+
// Process temp, append to results
|
|
98
|
+
all_results.extend(temp.drain(..).filter(|p| p.is_valid()));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
all_results
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Supported Collections
|
|
106
|
+
|
|
107
|
+
`collect_into()` works with any type implementing `Extend`:
|
|
108
|
+
|
|
109
|
+
```rust
|
|
110
|
+
use std::collections::{HashSet, HashMap, VecDeque};
|
|
111
|
+
|
|
112
|
+
let mut vec = Vec::new();
|
|
113
|
+
let mut set = HashSet::new();
|
|
114
|
+
let mut deque = VecDeque::new();
|
|
115
|
+
|
|
116
|
+
(0..10).collect_into(&mut vec);
|
|
117
|
+
(0..10).collect_into(&mut set);
|
|
118
|
+
(0..10).collect_into(&mut deque);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Comparison
|
|
122
|
+
|
|
123
|
+
| Method | Allocation | Buffer Reuse |
|
|
124
|
+
|--------|------------|--------------|
|
|
125
|
+
| `.collect()` | New each time | No |
|
|
126
|
+
| `.collect_into(&mut buf)` | Reuses buffer | Yes |
|
|
127
|
+
| `buf.extend(iter)` | Reuses buffer | Yes |
|
|
128
|
+
|
|
129
|
+
## See Also
|
|
130
|
+
|
|
131
|
+
- [perf-drain-reuse](./perf-drain-reuse.md) - Drain for reuse
|
|
132
|
+
- [mem-reuse-collections](./mem-reuse-collections.md) - Collection reuse
|
|
133
|
+
- [perf-extend-batch](./perf-extend-batch.md) - Batch extensions
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# perf-collect-once
|
|
2
|
+
|
|
3
|
+
> Don't collect intermediate iterators
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Each `.collect()` allocates a new collection. Chaining multiple operations with intermediate collections wastes memory and CPU cycles. Keep iterator chains lazy and collect only once at the end.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Three allocations, three passes
|
|
13
|
+
fn process_users(users: Vec<User>) -> Vec<String> {
|
|
14
|
+
let active: Vec<_> = users.into_iter()
|
|
15
|
+
.filter(|u| u.is_active)
|
|
16
|
+
.collect();
|
|
17
|
+
|
|
18
|
+
let verified: Vec<_> = active.into_iter()
|
|
19
|
+
.filter(|u| u.is_verified)
|
|
20
|
+
.collect();
|
|
21
|
+
|
|
22
|
+
verified.into_iter()
|
|
23
|
+
.map(|u| u.name)
|
|
24
|
+
.collect()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Collecting to count
|
|
28
|
+
fn count_valid(items: &[Item]) -> usize {
|
|
29
|
+
items.iter()
|
|
30
|
+
.filter(|i| i.is_valid())
|
|
31
|
+
.collect::<Vec<_>>() // Unnecessary!
|
|
32
|
+
.len()
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Good
|
|
37
|
+
|
|
38
|
+
```rust
|
|
39
|
+
// One allocation, one pass
|
|
40
|
+
fn process_users(users: Vec<User>) -> Vec<String> {
|
|
41
|
+
users.into_iter()
|
|
42
|
+
.filter(|u| u.is_active)
|
|
43
|
+
.filter(|u| u.is_verified)
|
|
44
|
+
.map(|u| u.name)
|
|
45
|
+
.collect()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// No allocation needed
|
|
49
|
+
fn count_valid(items: &[Item]) -> usize {
|
|
50
|
+
items.iter()
|
|
51
|
+
.filter(|i| i.is_valid())
|
|
52
|
+
.count()
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Pattern: Deferred Collection
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
// Create the iterator chain
|
|
60
|
+
fn prepare_data(raw: Vec<RawData>) -> impl Iterator<Item = ProcessedData> {
|
|
61
|
+
raw.into_iter()
|
|
62
|
+
.filter(|d| d.is_valid())
|
|
63
|
+
.map(ProcessedData::from)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Collect only when needed
|
|
67
|
+
let data: Vec<_> = prepare_data(input).collect();
|
|
68
|
+
|
|
69
|
+
// Or consume without collecting
|
|
70
|
+
prepare_data(input).for_each(|d| process(d));
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## When Intermediate Collection Is Needed
|
|
74
|
+
|
|
75
|
+
```rust
|
|
76
|
+
// Need to iterate multiple times
|
|
77
|
+
let items: Vec<_> = data.iter()
|
|
78
|
+
.filter(|x| x.is_valid())
|
|
79
|
+
.collect();
|
|
80
|
+
|
|
81
|
+
let count = items.len();
|
|
82
|
+
let first = items.first();
|
|
83
|
+
for item in &items {
|
|
84
|
+
process(item);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Need to sort (requires concrete collection)
|
|
88
|
+
let mut sorted: Vec<_> = data.iter()
|
|
89
|
+
.filter(|x| x.is_active)
|
|
90
|
+
.collect();
|
|
91
|
+
sorted.sort_by_key(|x| x.priority);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Comparison
|
|
95
|
+
|
|
96
|
+
| Approach | Allocations | Passes | Memory |
|
|
97
|
+
|----------|-------------|--------|--------|
|
|
98
|
+
| Multiple `.collect()` | N | N | O(N × data) |
|
|
99
|
+
| Single chain + `.collect()` | 1 | 1 | O(data) |
|
|
100
|
+
| No `.collect()` (streaming) | 0 | 1 | O(1) |
|
|
101
|
+
|
|
102
|
+
## Pattern: Collect with Capacity
|
|
103
|
+
|
|
104
|
+
When you must collect, pre-allocate:
|
|
105
|
+
|
|
106
|
+
```rust
|
|
107
|
+
// With estimated capacity
|
|
108
|
+
let mut result = Vec::with_capacity(items.len());
|
|
109
|
+
result.extend(
|
|
110
|
+
items.iter()
|
|
111
|
+
.filter(|x| x.is_valid())
|
|
112
|
+
.map(|x| x.clone())
|
|
113
|
+
);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## See Also
|
|
117
|
+
|
|
118
|
+
- [perf-iter-lazy](./perf-iter-lazy.md) - Keep iterators lazy
|
|
119
|
+
- [mem-with-capacity](./mem-with-capacity.md) - Pre-allocate collections
|
|
120
|
+
- [anti-collect-intermediate](./anti-collect-intermediate.md) - Anti-pattern
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# perf-drain-reuse
|
|
2
|
+
|
|
3
|
+
> Use drain to reuse allocations
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`drain()` removes elements from a collection while keeping its allocated capacity. This allows reusing the same allocation across iterations, avoiding repeated allocate/deallocate cycles in loops.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Allocates new Vec every iteration
|
|
13
|
+
fn process_batches(data: Vec<Item>) {
|
|
14
|
+
let mut remaining = data;
|
|
15
|
+
|
|
16
|
+
while !remaining.is_empty() {
|
|
17
|
+
let batch: Vec<_> = remaining.drain(..100.min(remaining.len())).collect();
|
|
18
|
+
process_batch(batch);
|
|
19
|
+
// remaining keeps its capacity - good
|
|
20
|
+
// but batch allocates new every time - bad
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Clears and reallocates
|
|
25
|
+
fn reuse_buffer() {
|
|
26
|
+
for _ in 0..1000 {
|
|
27
|
+
let mut buffer = Vec::new(); // Allocates each iteration
|
|
28
|
+
fill_buffer(&mut buffer);
|
|
29
|
+
process(&buffer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Good
|
|
35
|
+
|
|
36
|
+
```rust
|
|
37
|
+
// Reuses allocation with drain
|
|
38
|
+
fn process_batches(mut data: Vec<Item>) {
|
|
39
|
+
let mut batch = Vec::with_capacity(100);
|
|
40
|
+
|
|
41
|
+
while !data.is_empty() {
|
|
42
|
+
batch.extend(data.drain(..100.min(data.len())));
|
|
43
|
+
process_batch(&batch);
|
|
44
|
+
batch.clear(); // Keeps capacity
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Reuses buffer across iterations
|
|
49
|
+
fn reuse_buffer() {
|
|
50
|
+
let mut buffer = Vec::new();
|
|
51
|
+
|
|
52
|
+
for _ in 0..1000 {
|
|
53
|
+
buffer.clear(); // Keeps capacity
|
|
54
|
+
fill_buffer(&mut buffer);
|
|
55
|
+
process(&buffer);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Drain Methods
|
|
61
|
+
|
|
62
|
+
| Collection | Method | Behavior |
|
|
63
|
+
|------------|--------|----------|
|
|
64
|
+
| `Vec<T>` | `.drain(range)` | Remove range, shift remaining |
|
|
65
|
+
| `Vec<T>` | `.drain(..)` | Remove all (like clear) |
|
|
66
|
+
| `VecDeque<T>` | `.drain(range)` | Remove range |
|
|
67
|
+
| `String` | `.drain(range)` | Remove char range |
|
|
68
|
+
| `HashMap<K,V>` | `.drain()` | Remove all entries |
|
|
69
|
+
| `HashSet<T>` | `.drain()` | Remove all elements |
|
|
70
|
+
|
|
71
|
+
## Pattern: Batch Processing
|
|
72
|
+
|
|
73
|
+
```rust
|
|
74
|
+
fn process_in_chunks(mut items: Vec<Item>, chunk_size: usize) {
|
|
75
|
+
while !items.is_empty() {
|
|
76
|
+
let chunk: Vec<_> = items.drain(..chunk_size.min(items.len())).collect();
|
|
77
|
+
process_chunk(chunk);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Pattern: Transfer Between Collections
|
|
83
|
+
|
|
84
|
+
```rust
|
|
85
|
+
// Move all elements without reallocation
|
|
86
|
+
fn transfer_all(src: &mut Vec<Item>, dst: &mut Vec<Item>) {
|
|
87
|
+
dst.extend(src.drain(..));
|
|
88
|
+
// src is now empty but keeps capacity
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Move matching elements
|
|
92
|
+
fn transfer_matching(src: &mut Vec<Item>, dst: &mut Vec<Item>, predicate: impl Fn(&Item) -> bool) {
|
|
93
|
+
let matching: Vec<_> = src.drain(..).filter(predicate).collect();
|
|
94
|
+
dst.extend(matching);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Pattern: HashMap Drain
|
|
99
|
+
|
|
100
|
+
```rust
|
|
101
|
+
use std::collections::HashMap;
|
|
102
|
+
|
|
103
|
+
fn process_and_clear(map: &mut HashMap<String, Value>) {
|
|
104
|
+
// Process all entries, clearing the map
|
|
105
|
+
for (key, value) in map.drain() {
|
|
106
|
+
process(key, value);
|
|
107
|
+
}
|
|
108
|
+
// map is now empty but keeps capacity
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## drain vs clear vs take
|
|
113
|
+
|
|
114
|
+
| Operation | Elements | Capacity | Returns |
|
|
115
|
+
|-----------|----------|----------|---------|
|
|
116
|
+
| `.clear()` | Removed | Kept | Nothing |
|
|
117
|
+
| `.drain(..)` | Removed | Kept | Iterator |
|
|
118
|
+
| `std::mem::take()` | Moved out | Reset to 0 | Owned collection |
|
|
119
|
+
|
|
120
|
+
```rust
|
|
121
|
+
// clear: just empty
|
|
122
|
+
vec.clear();
|
|
123
|
+
|
|
124
|
+
// drain: empty and iterate
|
|
125
|
+
for item in vec.drain(..) {
|
|
126
|
+
process(item);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// take: swap with empty, get ownership
|
|
130
|
+
let old_vec = std::mem::take(&mut vec);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## See Also
|
|
134
|
+
|
|
135
|
+
- [mem-reuse-collections](./mem-reuse-collections.md) - Reusing collections
|
|
136
|
+
- [perf-extend-batch](./perf-extend-batch.md) - Batch insertions
|
|
137
|
+
- [mem-with-capacity](./mem-with-capacity.md) - Pre-allocation
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# perf-entry-api
|
|
2
|
+
|
|
3
|
+
> Use entry API for map insert-or-update
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
The entry API performs a single lookup for insert-or-update operations. Without it, you lookup twice: once to check existence, once to insert. For `HashMap` and `BTreeMap`, the entry API is both faster and more idiomatic.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
use std::collections::HashMap;
|
|
13
|
+
|
|
14
|
+
// Double lookup: contains_key + insert
|
|
15
|
+
fn increment(map: &mut HashMap<String, u32>, key: String) {
|
|
16
|
+
if map.contains_key(&key) {
|
|
17
|
+
*map.get_mut(&key).unwrap() += 1;
|
|
18
|
+
} else {
|
|
19
|
+
map.insert(key, 1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Double lookup with get + insert
|
|
24
|
+
fn get_or_insert(map: &mut HashMap<String, Vec<i32>>, key: String) -> &mut Vec<i32> {
|
|
25
|
+
if !map.contains_key(&key) {
|
|
26
|
+
map.insert(key.clone(), Vec::new());
|
|
27
|
+
}
|
|
28
|
+
map.get_mut(&key).unwrap()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Triple lookup pattern
|
|
32
|
+
fn update_or_default(map: &mut HashMap<String, Config>, key: &str, value: i32) {
|
|
33
|
+
match map.get(key) {
|
|
34
|
+
Some(config) => {
|
|
35
|
+
let mut new_config = config.clone();
|
|
36
|
+
new_config.value = value;
|
|
37
|
+
map.insert(key.to_string(), new_config);
|
|
38
|
+
}
|
|
39
|
+
None => {
|
|
40
|
+
map.insert(key.to_string(), Config::default());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Good
|
|
47
|
+
|
|
48
|
+
```rust
|
|
49
|
+
use std::collections::HashMap;
|
|
50
|
+
use std::collections::hash_map::Entry;
|
|
51
|
+
|
|
52
|
+
// Single lookup with entry
|
|
53
|
+
fn increment(map: &mut HashMap<String, u32>, key: String) {
|
|
54
|
+
*map.entry(key).or_insert(0) += 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Single lookup, returns mutable reference
|
|
58
|
+
fn get_or_insert(map: &mut HashMap<String, Vec<i32>>, key: String) -> &mut Vec<i32> {
|
|
59
|
+
map.entry(key).or_insert_with(Vec::new)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Single lookup with and_modify
|
|
63
|
+
fn update_or_default(map: &mut HashMap<String, Config>, key: String, value: i32) {
|
|
64
|
+
map.entry(key)
|
|
65
|
+
.and_modify(|config| config.value = value)
|
|
66
|
+
.or_insert_with(Config::default);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Entry API Methods
|
|
71
|
+
|
|
72
|
+
| Method | Behavior |
|
|
73
|
+
|--------|----------|
|
|
74
|
+
| `.or_insert(val)` | Insert `val` if empty |
|
|
75
|
+
| `.or_insert_with(f)` | Insert `f()` if empty (lazy) |
|
|
76
|
+
| `.or_default()` | Insert `Default::default()` if empty |
|
|
77
|
+
| `.and_modify(f)` | Apply `f` if occupied |
|
|
78
|
+
| `.or_insert_with_key(f)` | Insert `f(&key)` if empty |
|
|
79
|
+
|
|
80
|
+
## Pattern: Count Occurrences
|
|
81
|
+
|
|
82
|
+
```rust
|
|
83
|
+
fn word_count(text: &str) -> HashMap<&str, usize> {
|
|
84
|
+
let mut counts = HashMap::new();
|
|
85
|
+
for word in text.split_whitespace() {
|
|
86
|
+
*counts.entry(word).or_insert(0) += 1;
|
|
87
|
+
}
|
|
88
|
+
counts
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Pattern: Group By
|
|
93
|
+
|
|
94
|
+
```rust
|
|
95
|
+
fn group_by_category(items: Vec<Item>) -> HashMap<Category, Vec<Item>> {
|
|
96
|
+
let mut groups: HashMap<Category, Vec<Item>> = HashMap::new();
|
|
97
|
+
for item in items {
|
|
98
|
+
groups.entry(item.category.clone())
|
|
99
|
+
.or_default()
|
|
100
|
+
.push(item);
|
|
101
|
+
}
|
|
102
|
+
groups
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Pattern: Complex Entry Logic
|
|
107
|
+
|
|
108
|
+
```rust
|
|
109
|
+
match map.entry(key) {
|
|
110
|
+
Entry::Occupied(mut entry) => {
|
|
111
|
+
let value = entry.get_mut();
|
|
112
|
+
if should_update(value) {
|
|
113
|
+
*value = new_value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
Entry::Vacant(entry) => {
|
|
117
|
+
entry.insert(default_value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Performance
|
|
123
|
+
|
|
124
|
+
| Pattern | Lookups | Hash Computations |
|
|
125
|
+
|---------|---------|-------------------|
|
|
126
|
+
| `contains_key` + `insert` | 2 | 2 |
|
|
127
|
+
| `get` + `insert` | 2 | 2 |
|
|
128
|
+
| `entry().or_insert()` | 1 | 1 |
|
|
129
|
+
|
|
130
|
+
## See Also
|
|
131
|
+
|
|
132
|
+
- [perf-extend-batch](./perf-extend-batch.md) - Batch insertions
|
|
133
|
+
- [mem-with-capacity](./mem-with-capacity.md) - Pre-allocate maps
|
|
134
|
+
- [perf-drain-reuse](./perf-drain-reuse.md) - Reuse map allocations
|