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
package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
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
|
|
@@ -0,0 +1,158 @@
|
|
|
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
|
package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# async-joinset-structured
|
|
2
|
+
|
|
3
|
+
> Use `JoinSet` for managing dynamic collections of spawned tasks
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
When spawning a variable number of tasks, collecting `JoinHandle`s in a `Vec` and using `join_all` works but lacks flexibility. `JoinSet` provides a better abstraction: add/remove tasks dynamically, get results as they complete, and abort all on drop. It's the idiomatic way to manage task collections.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Manual handle management
|
|
13
|
+
let mut handles: Vec<JoinHandle<Result<Data>>> = Vec::new();
|
|
14
|
+
|
|
15
|
+
for url in urls {
|
|
16
|
+
handles.push(tokio::spawn(fetch(url)));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Wait for all, in order (not as they complete)
|
|
20
|
+
let results = futures::future::join_all(handles).await;
|
|
21
|
+
|
|
22
|
+
// No easy way to cancel all, handle errors progressively, or add more tasks
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Good
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
use tokio::task::JoinSet;
|
|
29
|
+
|
|
30
|
+
let mut set = JoinSet::new();
|
|
31
|
+
|
|
32
|
+
for url in urls {
|
|
33
|
+
set.spawn(fetch(url.clone()));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Process results as they complete
|
|
37
|
+
while let Some(result) = set.join_next().await {
|
|
38
|
+
match result {
|
|
39
|
+
Ok(Ok(data)) => process(data),
|
|
40
|
+
Ok(Err(e)) => log::error!("Task failed: {}", e),
|
|
41
|
+
Err(e) => log::error!("Task panicked: {}", e),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// All tasks done, set is empty
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Dynamic Task Addition
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
use tokio::task::JoinSet;
|
|
52
|
+
|
|
53
|
+
async fn worker_pool(mut rx: mpsc::Receiver<Task>) {
|
|
54
|
+
let mut set = JoinSet::new();
|
|
55
|
+
let max_concurrent = 10;
|
|
56
|
+
|
|
57
|
+
loop {
|
|
58
|
+
tokio::select! {
|
|
59
|
+
// Accept new tasks if under limit
|
|
60
|
+
Some(task) = rx.recv(), if set.len() < max_concurrent => {
|
|
61
|
+
set.spawn(process_task(task));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Process completed tasks
|
|
65
|
+
Some(result) = set.join_next() => {
|
|
66
|
+
handle_result(result);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Exit when no tasks and channel closed
|
|
70
|
+
else => break,
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Abort on Drop
|
|
77
|
+
|
|
78
|
+
```rust
|
|
79
|
+
use tokio::task::JoinSet;
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
let mut set = JoinSet::new();
|
|
83
|
+
set.spawn(long_running_task());
|
|
84
|
+
set.spawn(another_task());
|
|
85
|
+
|
|
86
|
+
// Early exit
|
|
87
|
+
return;
|
|
88
|
+
} // JoinSet dropped here - all tasks are aborted!
|
|
89
|
+
|
|
90
|
+
// Explicit abort
|
|
91
|
+
let mut set = JoinSet::new();
|
|
92
|
+
set.spawn(task());
|
|
93
|
+
set.abort_all(); // Cancel all tasks
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Error Handling Pattern
|
|
97
|
+
|
|
98
|
+
```rust
|
|
99
|
+
use tokio::task::JoinSet;
|
|
100
|
+
|
|
101
|
+
async fn fetch_all(urls: &[String]) -> Vec<Result<Data, Error>> {
|
|
102
|
+
let mut set = JoinSet::new();
|
|
103
|
+
let mut results = Vec::new();
|
|
104
|
+
|
|
105
|
+
for url in urls {
|
|
106
|
+
set.spawn(fetch(url.clone()));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
while let Some(join_result) = set.join_next().await {
|
|
110
|
+
let result = match join_result {
|
|
111
|
+
Ok(task_result) => task_result,
|
|
112
|
+
Err(join_error) => {
|
|
113
|
+
if join_error.is_panic() {
|
|
114
|
+
Err(Error::TaskPanicked)
|
|
115
|
+
} else {
|
|
116
|
+
Err(Error::TaskCancelled)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
results.push(result);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
results
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## With Cancellation
|
|
128
|
+
|
|
129
|
+
```rust
|
|
130
|
+
use tokio::task::JoinSet;
|
|
131
|
+
use tokio_util::sync::CancellationToken;
|
|
132
|
+
|
|
133
|
+
async fn run_workers(shutdown: CancellationToken) {
|
|
134
|
+
let mut set = JoinSet::new();
|
|
135
|
+
|
|
136
|
+
for i in 0..4 {
|
|
137
|
+
let token = shutdown.child_token();
|
|
138
|
+
set.spawn(async move {
|
|
139
|
+
loop {
|
|
140
|
+
tokio::select! {
|
|
141
|
+
_ = token.cancelled() => break,
|
|
142
|
+
_ = do_work(i) => {}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Wait for shutdown
|
|
149
|
+
shutdown.cancelled().await;
|
|
150
|
+
|
|
151
|
+
// Abort remaining tasks
|
|
152
|
+
set.abort_all();
|
|
153
|
+
|
|
154
|
+
// Wait for all to finish (drain aborted tasks)
|
|
155
|
+
while set.join_next().await.is_some() {}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Spawning with Context
|
|
160
|
+
|
|
161
|
+
```rust
|
|
162
|
+
use tokio::task::JoinSet;
|
|
163
|
+
|
|
164
|
+
let mut set: JoinSet<(usize, Result<Data, Error>)> = JoinSet::new();
|
|
165
|
+
|
|
166
|
+
for (index, url) in urls.iter().enumerate() {
|
|
167
|
+
let url = url.clone();
|
|
168
|
+
set.spawn(async move {
|
|
169
|
+
(index, fetch(&url).await)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Results include their index
|
|
174
|
+
while let Some(result) = set.join_next().await {
|
|
175
|
+
if let Ok((index, data)) = result {
|
|
176
|
+
results[index] = Some(data);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## JoinSet vs join_all
|
|
182
|
+
|
|
183
|
+
| Feature | JoinSet | join_all |
|
|
184
|
+
|---------|---------|----------|
|
|
185
|
+
| Add tasks dynamically | Yes | No |
|
|
186
|
+
| Results as-completed | Yes | No (all at once) |
|
|
187
|
+
| Abort all on drop | Yes | No |
|
|
188
|
+
| Cancel individual | Yes | No |
|
|
189
|
+
| Memory efficient | Yes | Pre-allocates |
|
|
190
|
+
|
|
191
|
+
## See Also
|
|
192
|
+
|
|
193
|
+
- [async-join-parallel](./async-join-parallel.md) - Static concurrent futures
|
|
194
|
+
- [async-cancellation-token](./async-cancellation-token.md) - Cancellation patterns
|
|
195
|
+
- [async-try-join](./async-try-join.md) - Error handling in joins
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# async-mpsc-queue
|
|
2
|
+
|
|
3
|
+
> Use `mpsc` channels for async message queues between tasks
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`tokio::sync::mpsc` (multi-producer, single-consumer) is the workhorse channel for async Rust. It provides async send/receive, backpressure via bounded capacity, and efficient cloning of senders. It's the default choice for task-to-task communication.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
use std::sync::mpsc; // Wrong! Blocks the async runtime
|
|
13
|
+
|
|
14
|
+
let (tx, rx) = std::sync::mpsc::channel();
|
|
15
|
+
|
|
16
|
+
tokio::spawn(async move {
|
|
17
|
+
tx.send("hello").unwrap(); // Might block
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
tokio::spawn(async move {
|
|
21
|
+
let msg = rx.recv().unwrap(); // BLOCKS the executor thread!
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Good
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
use tokio::sync::mpsc;
|
|
29
|
+
|
|
30
|
+
let (tx, mut rx) = mpsc::channel::<String>(100);
|
|
31
|
+
|
|
32
|
+
tokio::spawn(async move {
|
|
33
|
+
tx.send("hello".to_string()).await.unwrap();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
tokio::spawn(async move {
|
|
37
|
+
while let Some(msg) = rx.recv().await {
|
|
38
|
+
println!("Received: {}", msg);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Sender Cloning
|
|
44
|
+
|
|
45
|
+
```rust
|
|
46
|
+
use tokio::sync::mpsc;
|
|
47
|
+
|
|
48
|
+
let (tx, mut rx) = mpsc::channel::<Event>(100);
|
|
49
|
+
|
|
50
|
+
// Multiple producers
|
|
51
|
+
for i in 0..10 {
|
|
52
|
+
let tx = tx.clone(); // Cheap clone
|
|
53
|
+
tokio::spawn(async move {
|
|
54
|
+
tx.send(Event { source: i }).await.unwrap();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Drop original sender so channel closes when all clones dropped
|
|
59
|
+
drop(tx);
|
|
60
|
+
|
|
61
|
+
// Consumer
|
|
62
|
+
while let Some(event) = rx.recv().await {
|
|
63
|
+
process(event);
|
|
64
|
+
}
|
|
65
|
+
// Loop exits when all senders dropped
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Message Handler Pattern
|
|
69
|
+
|
|
70
|
+
```rust
|
|
71
|
+
use tokio::sync::mpsc;
|
|
72
|
+
|
|
73
|
+
enum Command {
|
|
74
|
+
Get { key: String, reply: oneshot::Sender<Option<Value>> },
|
|
75
|
+
Set { key: String, value: Value },
|
|
76
|
+
Delete { key: String },
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async fn run_store(mut commands: mpsc::Receiver<Command>) {
|
|
80
|
+
let mut store = HashMap::new();
|
|
81
|
+
|
|
82
|
+
while let Some(cmd) = commands.recv().await {
|
|
83
|
+
match cmd {
|
|
84
|
+
Command::Get { key, reply } => {
|
|
85
|
+
let _ = reply.send(store.get(&key).cloned());
|
|
86
|
+
}
|
|
87
|
+
Command::Set { key, value } => {
|
|
88
|
+
store.insert(key, value);
|
|
89
|
+
}
|
|
90
|
+
Command::Delete { key } => {
|
|
91
|
+
store.remove(&key);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Usage
|
|
98
|
+
async fn client(tx: mpsc::Sender<Command>) -> Option<Value> {
|
|
99
|
+
let (reply_tx, reply_rx) = oneshot::channel();
|
|
100
|
+
|
|
101
|
+
tx.send(Command::Get {
|
|
102
|
+
key: "foo".to_string(),
|
|
103
|
+
reply: reply_tx
|
|
104
|
+
}).await.unwrap();
|
|
105
|
+
|
|
106
|
+
reply_rx.await.unwrap()
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Graceful Shutdown
|
|
111
|
+
|
|
112
|
+
```rust
|
|
113
|
+
async fn worker(mut rx: mpsc::Receiver<Task>, shutdown: CancellationToken) {
|
|
114
|
+
loop {
|
|
115
|
+
tokio::select! {
|
|
116
|
+
_ = shutdown.cancelled() => {
|
|
117
|
+
// Drain remaining messages
|
|
118
|
+
while let Ok(task) = rx.try_recv() {
|
|
119
|
+
process(task).await;
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
Some(task) = rx.recv() => {
|
|
124
|
+
process(task).await;
|
|
125
|
+
}
|
|
126
|
+
else => break, // Channel closed
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## WeakSender for Optional Producers
|
|
133
|
+
|
|
134
|
+
```rust
|
|
135
|
+
use tokio::sync::mpsc;
|
|
136
|
+
|
|
137
|
+
let (tx, mut rx) = mpsc::channel::<Message>(100);
|
|
138
|
+
let weak = tx.downgrade(); // Doesn't keep channel alive
|
|
139
|
+
|
|
140
|
+
tokio::spawn(async move {
|
|
141
|
+
// Strong sender - keeps channel alive
|
|
142
|
+
tx.send("from strong".into()).await.unwrap();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
tokio::spawn(async move {
|
|
146
|
+
// Weak sender - may fail if strong senders dropped
|
|
147
|
+
if let Some(tx) = weak.upgrade() {
|
|
148
|
+
tx.send("from weak".into()).await.unwrap();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Permit Pattern
|
|
154
|
+
|
|
155
|
+
```rust
|
|
156
|
+
// Reserve slot before preparing message
|
|
157
|
+
let permit = tx.reserve().await?;
|
|
158
|
+
|
|
159
|
+
// Now we have guaranteed capacity
|
|
160
|
+
let message = expensive_to_create_message();
|
|
161
|
+
permit.send(message); // Never fails
|
|
162
|
+
|
|
163
|
+
// Useful when message creation is expensive
|
|
164
|
+
// and you don't want to create it if channel is full
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## See Also
|
|
168
|
+
|
|
169
|
+
- [async-bounded-channel](./async-bounded-channel.md) - Why bounded channels
|
|
170
|
+
- [async-oneshot-response](./async-oneshot-response.md) - Request-response with oneshot
|
|
171
|
+
- [async-broadcast-pubsub](./async-broadcast-pubsub.md) - Multiple consumers
|