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.
Files changed (185) hide show
  1. package/package.json +1 -1
  2. package/template/agent/rules/debug-confirmation-policy.md +34 -0
  3. package/template/agent/rules/language-matching.md +32 -0
  4. package/template/agent/skills/rust-developer/SKILL.md +281 -0
  5. package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
  6. package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
  7. package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
  8. package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
  9. package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
  10. package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
  11. package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
  12. package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
  13. package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
  14. package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
  15. package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
  16. package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
  17. package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
  18. package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
  19. package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
  20. package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
  21. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
  22. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
  23. package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
  24. package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
  25. package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
  26. package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
  27. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
  28. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
  29. package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
  30. package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
  31. package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
  32. package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
  33. package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
  34. package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
  35. package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
  36. package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
  37. package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
  38. package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
  39. package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
  40. package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
  41. package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
  42. package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
  43. package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
  44. package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
  45. package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
  46. package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
  47. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
  48. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
  49. package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
  50. package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
  51. package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
  52. package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
  53. package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
  54. package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
  55. package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
  56. package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
  57. package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
  58. package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
  59. package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
  60. package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
  61. package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
  62. package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
  63. package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
  64. package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
  65. package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
  66. package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
  67. package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
  68. package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
  69. package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
  70. package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
  71. package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
  72. package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
  73. package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
  74. package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
  75. package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
  76. package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
  77. package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
  78. package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
  79. package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
  80. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
  81. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
  82. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
  83. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
  84. package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
  85. package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
  86. package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
  87. package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
  88. package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
  89. package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
  90. package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
  91. package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
  92. package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
  93. package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
  94. package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
  95. package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
  96. package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
  97. package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
  98. package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
  99. package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
  100. package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
  101. package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
  102. package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
  103. package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
  104. package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
  105. package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
  106. package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
  107. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
  108. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
  109. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
  110. package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
  111. package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
  112. package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
  113. package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
  114. package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
  115. package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
  116. package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
  117. package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
  118. package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
  119. package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
  120. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
  121. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
  122. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
  123. package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
  124. package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
  125. package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
  126. package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
  127. package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
  128. package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
  129. package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
  130. package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
  131. package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
  132. package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
  133. package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
  134. package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
  135. package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
  136. package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
  137. package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
  138. package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
  139. package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
  140. package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
  141. package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
  142. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
  143. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
  144. package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
  145. package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
  146. package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
  147. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
  148. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
  149. package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
  150. package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
  151. package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
  152. package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
  153. package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
  154. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
  155. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
  156. package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
  157. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
  158. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
  159. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
  160. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
  161. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
  162. package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
  163. package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
  164. package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
  165. package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
  166. package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
  167. package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
  168. package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
  169. package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
  170. package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
  171. package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
  172. package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
  173. package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
  174. package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
  175. package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
  176. package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
  177. package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
  178. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
  179. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
  180. package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
  181. package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
  182. package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
  183. package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
  184. package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
  185. package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
@@ -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
@@ -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