agy-superpowers 5.2.1 → 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/patches/skills-patches.md +23 -0
- package/template/agent/rules/scratch-scripts.md +37 -0
- package/template/agent/rules/superpowers.md +6 -50
- package/template/agent/skills/brainstorming/SKILL.md +4 -3
- package/template/agent/skills/brainstorming/visual-companion.md +2 -3
- package/template/agent/skills/finishing-a-development-branch/SKILL.md +11 -16
- package/template/agent/skills/subagent-driven-development/SKILL.md +16 -0
- package/template/agent/skills/subagent-driven-development/implementer-prompt.md +4 -3
- package/template/agent/skills/using-git-worktrees/SKILL.md +3 -2
- package/template/agent/skills/using-superpowers/SKILL.md +8 -6
- package/template/agent/skills/using-superpowers/references/copilot-tools.md +52 -0
- package/template/agent/skills/writing-plans/SKILL.md +5 -3
- package/template/agent/skills/writing-skills/SKILL.md +1 -1
- package/template/agent/superpowers-version.json +2 -2
- package/template/agent/tmp/agent-config-backup.yml +9 -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
package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
# async-broadcast-pubsub
|
|
2
|
-
|
|
3
|
-
> Use `broadcast` channel for pub/sub where all subscribers receive all messages
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Unlike `mpsc` where one consumer receives each message, `broadcast` delivers each message to all subscribers. This is ideal for event broadcasting, real-time notifications, or when multiple components need to react to the same events independently.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
use tokio::sync::mpsc;
|
|
13
|
-
|
|
14
|
-
// mpsc only delivers to ONE consumer
|
|
15
|
-
let (tx, mut rx) = mpsc::channel::<Event>(100);
|
|
16
|
-
|
|
17
|
-
// Only one of these receives each message!
|
|
18
|
-
let mut rx2 = ???; // Can't clone receiver
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Good
|
|
22
|
-
|
|
23
|
-
```rust
|
|
24
|
-
use tokio::sync::broadcast;
|
|
25
|
-
|
|
26
|
-
// broadcast delivers to ALL subscribers
|
|
27
|
-
let (tx, _) = broadcast::channel::<Event>(100);
|
|
28
|
-
|
|
29
|
-
// Each subscriber gets ALL messages
|
|
30
|
-
let mut rx1 = tx.subscribe();
|
|
31
|
-
let mut rx2 = tx.subscribe();
|
|
32
|
-
|
|
33
|
-
tokio::spawn(async move {
|
|
34
|
-
while let Ok(event) = rx1.recv().await {
|
|
35
|
-
handle_in_logger(event);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
tokio::spawn(async move {
|
|
40
|
-
while let Ok(event) = rx2.recv().await {
|
|
41
|
-
handle_in_metrics(event);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Both subscribers receive this
|
|
46
|
-
tx.send(Event::UserLogin { user_id: 42 })?;
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Broadcast Semantics
|
|
50
|
-
|
|
51
|
-
```rust
|
|
52
|
-
use tokio::sync::broadcast;
|
|
53
|
-
|
|
54
|
-
let (tx, mut rx1) = broadcast::channel::<i32>(16);
|
|
55
|
-
let mut rx2 = tx.subscribe();
|
|
56
|
-
|
|
57
|
-
tx.send(1)?;
|
|
58
|
-
tx.send(2)?;
|
|
59
|
-
|
|
60
|
-
// Both receive all messages
|
|
61
|
-
assert_eq!(rx1.recv().await?, 1);
|
|
62
|
-
assert_eq!(rx1.recv().await?, 2);
|
|
63
|
-
assert_eq!(rx2.recv().await?, 1);
|
|
64
|
-
assert_eq!(rx2.recv().await?, 2);
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Handling Lagging Receivers
|
|
68
|
-
|
|
69
|
-
```rust
|
|
70
|
-
use tokio::sync::broadcast::{self, error::RecvError};
|
|
71
|
-
|
|
72
|
-
let (tx, mut rx) = broadcast::channel::<Event>(16);
|
|
73
|
-
|
|
74
|
-
loop {
|
|
75
|
-
match rx.recv().await {
|
|
76
|
-
Ok(event) => {
|
|
77
|
-
process(event);
|
|
78
|
-
}
|
|
79
|
-
Err(RecvError::Lagged(count)) => {
|
|
80
|
-
// Receiver couldn't keep up, missed `count` messages
|
|
81
|
-
log::warn!("Missed {} events", count);
|
|
82
|
-
// Continue receiving new messages
|
|
83
|
-
}
|
|
84
|
-
Err(RecvError::Closed) => {
|
|
85
|
-
break; // All senders dropped
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Event Bus Pattern
|
|
92
|
-
|
|
93
|
-
```rust
|
|
94
|
-
use tokio::sync::broadcast;
|
|
95
|
-
|
|
96
|
-
#[derive(Clone, Debug)]
|
|
97
|
-
enum AppEvent {
|
|
98
|
-
UserLoggedIn { user_id: u64 },
|
|
99
|
-
OrderCreated { order_id: u64 },
|
|
100
|
-
SystemShutdown,
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
struct EventBus {
|
|
104
|
-
tx: broadcast::Sender<AppEvent>,
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
impl EventBus {
|
|
108
|
-
fn new() -> Self {
|
|
109
|
-
let (tx, _) = broadcast::channel(1000);
|
|
110
|
-
EventBus { tx }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
fn publish(&self, event: AppEvent) {
|
|
114
|
-
// Ignore error if no subscribers
|
|
115
|
-
let _ = self.tx.send(event);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
fn subscribe(&self) -> broadcast::Receiver<AppEvent> {
|
|
119
|
-
self.tx.subscribe()
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Usage
|
|
124
|
-
let bus = EventBus::new();
|
|
125
|
-
|
|
126
|
-
// Logger subscribes
|
|
127
|
-
let mut log_rx = bus.subscribe();
|
|
128
|
-
tokio::spawn(async move {
|
|
129
|
-
while let Ok(event) = log_rx.recv().await {
|
|
130
|
-
log::info!("Event: {:?}", event);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// Metrics subscribes
|
|
135
|
-
let mut metrics_rx = bus.subscribe();
|
|
136
|
-
tokio::spawn(async move {
|
|
137
|
-
while let Ok(event) = metrics_rx.recv().await {
|
|
138
|
-
record_metric(&event);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Publish events
|
|
143
|
-
bus.publish(AppEvent::UserLoggedIn { user_id: 42 });
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Broadcast vs Watch
|
|
147
|
-
|
|
148
|
-
```rust
|
|
149
|
-
// broadcast: subscribers get ALL messages
|
|
150
|
-
// Good for: events, logs, notifications
|
|
151
|
-
let (tx, _) = broadcast::channel::<Event>(100);
|
|
152
|
-
|
|
153
|
-
// watch: subscribers get LATEST value only
|
|
154
|
-
// Good for: config changes, state updates
|
|
155
|
-
let (tx, _) = watch::channel(initial_state);
|
|
156
|
-
|
|
157
|
-
// If subscriber is slow:
|
|
158
|
-
// - broadcast: they receive old messages (or lag)
|
|
159
|
-
// - watch: they skip to latest (no history)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Clone Requirement
|
|
163
|
-
|
|
164
|
-
```rust
|
|
165
|
-
// broadcast requires Clone because message is cloned to each receiver
|
|
166
|
-
use tokio::sync::broadcast;
|
|
167
|
-
|
|
168
|
-
#[derive(Clone)] // Required for broadcast
|
|
169
|
-
struct Event {
|
|
170
|
-
data: String,
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
let (tx, _) = broadcast::channel::<Event>(100);
|
|
174
|
-
|
|
175
|
-
// For non-Clone types, wrap in Arc
|
|
176
|
-
use std::sync::Arc;
|
|
177
|
-
|
|
178
|
-
let (tx, _) = broadcast::channel::<Arc<LargeNonClone>>(100);
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## See Also
|
|
182
|
-
|
|
183
|
-
- [async-mpsc-queue](./async-mpsc-queue.md) - Single-consumer channels
|
|
184
|
-
- [async-watch-latest](./async-watch-latest.md) - Latest-value only
|
|
185
|
-
- [async-bounded-channel](./async-bounded-channel.md) - Buffer sizing
|
package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
# async-cancellation-token
|
|
2
|
-
|
|
3
|
-
> Use `CancellationToken` for graceful shutdown and task cancellation
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Dropping a `JoinHandle` doesn't cancel the task—it just detaches it. For graceful shutdown, you need explicit cancellation. `tokio_util::sync::CancellationToken` provides a cooperative cancellation mechanism that tasks can check and respond to, enabling clean resource cleanup.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
// Dropping handle doesn't stop the task
|
|
13
|
-
let handle = tokio::spawn(async {
|
|
14
|
-
loop {
|
|
15
|
-
do_work().await;
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
drop(handle); // Task continues running in background!
|
|
20
|
-
|
|
21
|
-
// Using bool flag - not async-aware
|
|
22
|
-
let running = Arc::new(AtomicBool::new(true));
|
|
23
|
-
|
|
24
|
-
tokio::spawn({
|
|
25
|
-
let running = running.clone();
|
|
26
|
-
async move {
|
|
27
|
-
while running.load(Ordering::Relaxed) {
|
|
28
|
-
do_work().await; // Can't wake up if blocked here
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
running.store(false, Ordering::Relaxed);
|
|
34
|
-
// Task won't stop until current do_work() completes
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Good
|
|
38
|
-
|
|
39
|
-
```rust
|
|
40
|
-
use tokio_util::sync::CancellationToken;
|
|
41
|
-
|
|
42
|
-
let token = CancellationToken::new();
|
|
43
|
-
|
|
44
|
-
let handle = tokio::spawn({
|
|
45
|
-
let token = token.clone();
|
|
46
|
-
async move {
|
|
47
|
-
loop {
|
|
48
|
-
tokio::select! {
|
|
49
|
-
_ = token.cancelled() => {
|
|
50
|
-
println!("Shutting down gracefully");
|
|
51
|
-
cleanup().await;
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
_ = do_work() => {
|
|
55
|
-
// Work completed
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Later: trigger cancellation
|
|
63
|
-
token.cancel();
|
|
64
|
-
handle.await?; // Task completes cleanly
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## CancellationToken API
|
|
68
|
-
|
|
69
|
-
```rust
|
|
70
|
-
use tokio_util::sync::CancellationToken;
|
|
71
|
-
|
|
72
|
-
// Create token
|
|
73
|
-
let token = CancellationToken::new();
|
|
74
|
-
|
|
75
|
-
// Clone for sharing (cheap Arc-based clone)
|
|
76
|
-
let token2 = token.clone();
|
|
77
|
-
|
|
78
|
-
// Check if cancelled (non-blocking)
|
|
79
|
-
if token.is_cancelled() {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Wait for cancellation (async)
|
|
84
|
-
token.cancelled().await;
|
|
85
|
-
|
|
86
|
-
// Trigger cancellation
|
|
87
|
-
token.cancel();
|
|
88
|
-
|
|
89
|
-
// Child tokens - cancelled when parent is cancelled
|
|
90
|
-
let child = token.child_token();
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Hierarchical Cancellation
|
|
94
|
-
|
|
95
|
-
```rust
|
|
96
|
-
async fn run_server(shutdown: CancellationToken) {
|
|
97
|
-
let listener = TcpListener::bind("0.0.0.0:8080").await?;
|
|
98
|
-
|
|
99
|
-
loop {
|
|
100
|
-
tokio::select! {
|
|
101
|
-
_ = shutdown.cancelled() => {
|
|
102
|
-
println!("Server shutting down");
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
result = listener.accept() => {
|
|
106
|
-
let (socket, _) = result?;
|
|
107
|
-
// Each connection gets child token
|
|
108
|
-
let conn_token = shutdown.child_token();
|
|
109
|
-
tokio::spawn(handle_connection(socket, conn_token));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Child tokens auto-cancelled when we exit
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async fn handle_connection(socket: TcpStream, token: CancellationToken) {
|
|
118
|
-
loop {
|
|
119
|
-
tokio::select! {
|
|
120
|
-
_ = token.cancelled() => {
|
|
121
|
-
// Connection cleanup
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
data = socket.read() => {
|
|
125
|
-
// Handle data
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Graceful Shutdown Pattern
|
|
133
|
-
|
|
134
|
-
```rust
|
|
135
|
-
use tokio::signal;
|
|
136
|
-
|
|
137
|
-
async fn main() -> Result<()> {
|
|
138
|
-
let shutdown = CancellationToken::new();
|
|
139
|
-
|
|
140
|
-
// Spawn signal handler
|
|
141
|
-
let shutdown_trigger = shutdown.clone();
|
|
142
|
-
tokio::spawn(async move {
|
|
143
|
-
signal::ctrl_c().await.expect("failed to listen for Ctrl+C");
|
|
144
|
-
println!("Received Ctrl+C, initiating shutdown...");
|
|
145
|
-
shutdown_trigger.cancel();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Run application with shutdown token
|
|
149
|
-
run_app(shutdown).await
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async fn run_app(shutdown: CancellationToken) -> Result<()> {
|
|
153
|
-
let mut tasks = JoinSet::new();
|
|
154
|
-
|
|
155
|
-
tasks.spawn(worker_task(shutdown.child_token()));
|
|
156
|
-
tasks.spawn(server_task(shutdown.child_token()));
|
|
157
|
-
|
|
158
|
-
// Wait for shutdown or task completion
|
|
159
|
-
tokio::select! {
|
|
160
|
-
_ = shutdown.cancelled() => {
|
|
161
|
-
println!("Shutdown requested, waiting for tasks...");
|
|
162
|
-
}
|
|
163
|
-
Some(result) = tasks.join_next() => {
|
|
164
|
-
// A task completed/failed
|
|
165
|
-
result??;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Wait for remaining tasks with timeout
|
|
170
|
-
tokio::time::timeout(
|
|
171
|
-
Duration::from_secs(30),
|
|
172
|
-
async { while tasks.join_next().await.is_some() {} }
|
|
173
|
-
).await.ok();
|
|
174
|
-
|
|
175
|
-
Ok(())
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## DropGuard Pattern
|
|
180
|
-
|
|
181
|
-
```rust
|
|
182
|
-
use tokio_util::sync::CancellationToken;
|
|
183
|
-
|
|
184
|
-
// Auto-cancel on drop
|
|
185
|
-
let token = CancellationToken::new();
|
|
186
|
-
let guard = token.clone().drop_guard();
|
|
187
|
-
|
|
188
|
-
tokio::spawn({
|
|
189
|
-
let token = token.clone();
|
|
190
|
-
async move {
|
|
191
|
-
token.cancelled().await;
|
|
192
|
-
println!("Cancelled!");
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
drop(guard); // Automatically calls token.cancel()
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
## See Also
|
|
200
|
-
|
|
201
|
-
- [async-joinset-structured](./async-joinset-structured.md) - Managing multiple tasks
|
|
202
|
-
- [async-select-racing](./async-select-racing.md) - select! for cancellation
|
|
203
|
-
- [async-tokio-runtime](./async-tokio-runtime.md) - Runtime shutdown
|
package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
# async-clone-before-await
|
|
2
|
-
|
|
3
|
-
> Clone Arc/Rc data before await points to avoid holding references across suspension
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
References held across `.await` points extend the future's lifetime and can cause borrow checker issues or prevent `Send` bounds. Cloning `Arc`/`Rc` before the await ensures the future only holds owned data, making it `Send` and avoiding lifetime complications.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
use std::sync::Arc;
|
|
13
|
-
|
|
14
|
-
async fn process(data: Arc<Data>) {
|
|
15
|
-
// Borrow extends across await - future is not Send
|
|
16
|
-
let slice = &data.items[..]; // Borrow of Arc contents
|
|
17
|
-
|
|
18
|
-
expensive_async_operation().await; // Await with active borrow
|
|
19
|
-
|
|
20
|
-
use_slice(slice); // Still using the borrow
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Error: future cannot be sent between threads safely
|
|
24
|
-
// because `&[Item]` cannot be sent between threads safely
|
|
25
|
-
tokio::spawn(process(data));
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Good
|
|
29
|
-
|
|
30
|
-
```rust
|
|
31
|
-
use std::sync::Arc;
|
|
32
|
-
|
|
33
|
-
async fn process(data: Arc<Data>) {
|
|
34
|
-
// Clone what you need before await
|
|
35
|
-
let items = data.items.clone(); // Owned Vec
|
|
36
|
-
|
|
37
|
-
expensive_async_operation().await;
|
|
38
|
-
|
|
39
|
-
use_items(&items); // Using owned data
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Or clone the Arc itself
|
|
43
|
-
async fn share_data(data: Arc<Data>) {
|
|
44
|
-
let data = data.clone(); // Another Arc handle
|
|
45
|
-
|
|
46
|
-
some_async_work().await;
|
|
47
|
-
|
|
48
|
-
process(&data); // Safe - we own the Arc
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## The Send Problem
|
|
53
|
-
|
|
54
|
-
```rust
|
|
55
|
-
// Futures must be Send to spawn on multi-threaded runtime
|
|
56
|
-
async fn not_send() {
|
|
57
|
-
let rc = Rc::new(42); // Rc is !Send
|
|
58
|
-
|
|
59
|
-
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
60
|
-
|
|
61
|
-
println!("{}", rc); // rc held across await
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
tokio::spawn(not_send()); // ERROR: future is not Send
|
|
65
|
-
|
|
66
|
-
// Fix: use Arc or don't hold across await
|
|
67
|
-
async fn is_send() {
|
|
68
|
-
let arc = Arc::new(42); // Arc is Send
|
|
69
|
-
|
|
70
|
-
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
71
|
-
|
|
72
|
-
println!("{}", arc);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
tokio::spawn(is_send()); // OK
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Minimizing Clones
|
|
79
|
-
|
|
80
|
-
```rust
|
|
81
|
-
// Bad: clone everything eagerly
|
|
82
|
-
async fn wasteful(data: Arc<LargeData>) {
|
|
83
|
-
let data = (*data).clone(); // Clones entire LargeData
|
|
84
|
-
async_work().await;
|
|
85
|
-
use_one_field(&data.small_field);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Good: clone only what you need
|
|
89
|
-
async fn efficient(data: Arc<LargeData>) {
|
|
90
|
-
let small = data.small_field.clone(); // Clone only needed field
|
|
91
|
-
async_work().await;
|
|
92
|
-
use_one_field(&small);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Good: if you need the whole thing, keep the Arc
|
|
96
|
-
async fn arc_efficient(data: Arc<LargeData>) {
|
|
97
|
-
let data = data.clone(); // Cheap Arc clone
|
|
98
|
-
async_work().await;
|
|
99
|
-
use_data(&data); // Access through Arc
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Spawn Pattern
|
|
104
|
-
|
|
105
|
-
```rust
|
|
106
|
-
// Common pattern: clone for spawned task
|
|
107
|
-
let shared = Arc::new(SharedState::new());
|
|
108
|
-
|
|
109
|
-
for i in 0..10 {
|
|
110
|
-
let shared = shared.clone(); // Clone before moving into spawn
|
|
111
|
-
tokio::spawn(async move {
|
|
112
|
-
// Task owns its Arc clone
|
|
113
|
-
shared.do_something(i).await;
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Scope-Based Approach
|
|
119
|
-
|
|
120
|
-
```rust
|
|
121
|
-
// Limit borrow scope to before await
|
|
122
|
-
async fn scoped(data: Arc<Data>) {
|
|
123
|
-
// Scope 1: borrow, compute, drop borrow
|
|
124
|
-
let computed = {
|
|
125
|
-
let slice = &data.items[..]; // Borrow
|
|
126
|
-
compute_something(slice) // Use
|
|
127
|
-
}; // Borrow ends here
|
|
128
|
-
|
|
129
|
-
// Now safe to await
|
|
130
|
-
expensive_async_operation().await;
|
|
131
|
-
|
|
132
|
-
use_computed(computed);
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## MutexGuard Across Await
|
|
137
|
-
|
|
138
|
-
```rust
|
|
139
|
-
use tokio::sync::Mutex;
|
|
140
|
-
|
|
141
|
-
// BAD: holding guard across await
|
|
142
|
-
async fn bad(mutex: Arc<Mutex<Data>>) {
|
|
143
|
-
let mut guard = mutex.lock().await;
|
|
144
|
-
guard.value += 1;
|
|
145
|
-
|
|
146
|
-
slow_operation().await; // Guard held during await!
|
|
147
|
-
|
|
148
|
-
guard.value += 1;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// GOOD: release before await
|
|
152
|
-
async fn good(mutex: Arc<Mutex<Data>>) {
|
|
153
|
-
{
|
|
154
|
-
let mut guard = mutex.lock().await;
|
|
155
|
-
guard.value += 1;
|
|
156
|
-
} // Guard released
|
|
157
|
-
|
|
158
|
-
slow_operation().await;
|
|
159
|
-
|
|
160
|
-
{
|
|
161
|
-
let mut guard = mutex.lock().await;
|
|
162
|
-
guard.value += 1;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## See Also
|
|
168
|
-
|
|
169
|
-
- [async-no-lock-await](./async-no-lock-await.md) - Lock guards across await
|
|
170
|
-
- [own-arc-shared](./own-arc-shared.md) - Arc usage patterns
|
|
171
|
-
- [async-spawn-blocking](./async-spawn-blocking.md) - Blocking in async
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# async-join-parallel
|
|
2
|
-
|
|
3
|
-
> Use `join!` or `try_join!` for concurrent independent futures
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Awaiting futures sequentially takes the sum of their durations. `join!` runs futures concurrently, taking only as long as the slowest one. For independent operations like multiple API calls or parallel file reads, this can dramatically reduce latency.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
async fn fetch_data() -> (User, Posts, Comments) {
|
|
13
|
-
// Sequential: 300ms total (100 + 100 + 100)
|
|
14
|
-
let user = fetch_user().await; // 100ms
|
|
15
|
-
let posts = fetch_posts().await; // 100ms
|
|
16
|
-
let comments = fetch_comments().await; // 100ms
|
|
17
|
-
|
|
18
|
-
(user, posts, comments)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async fn read_configs() -> Result<(Config, Settings)> {
|
|
22
|
-
// Sequential: 20ms + 20ms = 40ms
|
|
23
|
-
let config = fs::read_to_string("config.toml").await?;
|
|
24
|
-
let settings = fs::read_to_string("settings.json").await?;
|
|
25
|
-
|
|
26
|
-
Ok((parse_config(&config)?, parse_settings(&settings)?))
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Good
|
|
31
|
-
|
|
32
|
-
```rust
|
|
33
|
-
use tokio::join;
|
|
34
|
-
|
|
35
|
-
async fn fetch_data() -> (User, Posts, Comments) {
|
|
36
|
-
// Concurrent: ~100ms total (max of all three)
|
|
37
|
-
let (user, posts, comments) = join!(
|
|
38
|
-
fetch_user(),
|
|
39
|
-
fetch_posts(),
|
|
40
|
-
fetch_comments(),
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
(user, posts, comments)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
use tokio::try_join;
|
|
47
|
-
|
|
48
|
-
async fn read_configs() -> Result<(Config, Settings)> {
|
|
49
|
-
// Concurrent: ~20ms total
|
|
50
|
-
let (config_str, settings_str) = try_join!(
|
|
51
|
-
fs::read_to_string("config.toml"),
|
|
52
|
-
fs::read_to_string("settings.json"),
|
|
53
|
-
)?;
|
|
54
|
-
|
|
55
|
-
Ok((parse_config(&config_str)?, parse_settings(&settings_str)?))
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## join! vs try_join!
|
|
60
|
-
|
|
61
|
-
```rust
|
|
62
|
-
// join! - all futures run to completion, returns tuple
|
|
63
|
-
let (a, b, c) = join!(future_a, future_b, future_c);
|
|
64
|
-
|
|
65
|
-
// try_join! - short-circuits on first error
|
|
66
|
-
let (a, b, c) = try_join!(fallible_a, fallible_b, fallible_c)?;
|
|
67
|
-
// If fallible_b fails, returns Err immediately
|
|
68
|
-
// Other futures may still be running (cancellation is async)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## futures::join_all for Dynamic Collections
|
|
72
|
-
|
|
73
|
-
```rust
|
|
74
|
-
use futures::future::join_all;
|
|
75
|
-
|
|
76
|
-
async fn fetch_all_users(ids: &[u64]) -> Vec<User> {
|
|
77
|
-
let futures: Vec<_> = ids.iter()
|
|
78
|
-
.map(|id| fetch_user(*id))
|
|
79
|
-
.collect();
|
|
80
|
-
|
|
81
|
-
join_all(futures).await
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// With fallible futures
|
|
85
|
-
use futures::future::try_join_all;
|
|
86
|
-
|
|
87
|
-
async fn fetch_all_users(ids: &[u64]) -> Result<Vec<User>> {
|
|
88
|
-
let futures: Vec<_> = ids.iter()
|
|
89
|
-
.map(|id| fetch_user(*id))
|
|
90
|
-
.collect();
|
|
91
|
-
|
|
92
|
-
try_join_all(futures).await
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Limiting Concurrency
|
|
97
|
-
|
|
98
|
-
```rust
|
|
99
|
-
use futures::stream::{self, StreamExt};
|
|
100
|
-
|
|
101
|
-
async fn fetch_with_limit(ids: &[u64]) -> Vec<Result<User>> {
|
|
102
|
-
stream::iter(ids)
|
|
103
|
-
.map(|id| fetch_user(*id))
|
|
104
|
-
.buffer_unordered(10) // Max 10 concurrent requests
|
|
105
|
-
.collect()
|
|
106
|
-
.await
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Or with tokio::sync::Semaphore
|
|
110
|
-
use tokio::sync::Semaphore;
|
|
111
|
-
|
|
112
|
-
async fn fetch_with_semaphore(ids: &[u64]) -> Vec<User> {
|
|
113
|
-
let semaphore = Arc::new(Semaphore::new(10));
|
|
114
|
-
|
|
115
|
-
let futures: Vec<_> = ids.iter().map(|id| {
|
|
116
|
-
let semaphore = semaphore.clone();
|
|
117
|
-
async move {
|
|
118
|
-
let _permit = semaphore.acquire().await.unwrap();
|
|
119
|
-
fetch_user(*id).await
|
|
120
|
-
}
|
|
121
|
-
}).collect();
|
|
122
|
-
|
|
123
|
-
join_all(futures).await
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## When NOT to Use join!
|
|
128
|
-
|
|
129
|
-
```rust
|
|
130
|
-
// ❌ Dependent futures - must be sequential
|
|
131
|
-
async fn create_and_populate() -> Result<()> {
|
|
132
|
-
let db = create_database().await?; // Must complete first
|
|
133
|
-
populate_tables(&db).await?; // Depends on db
|
|
134
|
-
Ok(())
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// ❌ Short-circuiting logic
|
|
138
|
-
async fn find_first() -> Option<Data> {
|
|
139
|
-
// Want to stop when one succeeds
|
|
140
|
-
// Use select! instead
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ❌ Shared mutable state
|
|
144
|
-
async fn bad_shared_state() {
|
|
145
|
-
let counter = Arc::new(Mutex::new(0));
|
|
146
|
-
// This might work but can cause contention
|
|
147
|
-
join!(
|
|
148
|
-
increment(counter.clone()),
|
|
149
|
-
increment(counter.clone()),
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## See Also
|
|
155
|
-
|
|
156
|
-
- [async-try-join](./async-try-join.md) - Error handling in concurrent futures
|
|
157
|
-
- [async-select-racing](./async-select-racing.md) - Racing futures
|
|
158
|
-
- [async-joinset-structured](./async-joinset-structured.md) - Dynamic task sets
|