agy-superpowers 5.1.4 → 5.1.5

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 (182) hide show
  1. package/package.json +1 -1
  2. package/template/agent/skills/rust-developer/SKILL.md +281 -0
  3. package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
  4. package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
  5. package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
  6. package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
  7. package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
  8. package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
  9. package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
  10. package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
  11. package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
  12. package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
  13. package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
  14. package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
  15. package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
  16. package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
  17. package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
  18. package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
  19. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
  20. package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
  21. package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
  22. package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
  23. package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
  24. package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
  25. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
  26. package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
  27. package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
  28. package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
  29. package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
  30. package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
  31. package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
  32. package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
  33. package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
  34. package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
  35. package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
  36. package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
  37. package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
  38. package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
  39. package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
  40. package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
  41. package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
  42. package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
  43. package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
  44. package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
  45. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
  46. package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
  47. package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
  48. package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
  49. package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
  50. package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
  51. package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
  52. package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
  53. package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
  54. package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
  55. package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
  56. package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
  57. package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
  58. package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
  59. package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
  60. package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
  61. package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
  62. package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
  63. package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
  64. package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
  65. package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
  66. package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
  67. package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
  68. package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
  69. package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
  70. package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
  71. package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
  72. package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
  73. package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
  74. package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
  75. package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
  76. package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
  77. package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
  78. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
  79. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
  80. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
  81. package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
  82. package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
  83. package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
  84. package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
  85. package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
  86. package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
  87. package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
  88. package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
  89. package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
  90. package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
  91. package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
  92. package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
  93. package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
  94. package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
  95. package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
  96. package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
  97. package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
  98. package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
  99. package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
  100. package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
  101. package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
  102. package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
  103. package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
  104. package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
  105. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
  106. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
  107. package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
  108. package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
  109. package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
  110. package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
  111. package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
  112. package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
  113. package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
  114. package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
  115. package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
  116. package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
  117. package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
  118. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
  119. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
  120. package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
  121. package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
  122. package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
  123. package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
  124. package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
  125. package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
  126. package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
  127. package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
  128. package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
  129. package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
  130. package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
  131. package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
  132. package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
  133. package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
  134. package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
  135. package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
  136. package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
  137. package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
  138. package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
  139. package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
  140. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
  141. package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
  142. package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
  143. package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
  144. package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
  145. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
  146. package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
  147. package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
  148. package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
  149. package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
  150. package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
  151. package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
  152. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
  153. package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
  154. package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
  155. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
  156. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
  157. package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
  158. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
  159. package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
  160. package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
  161. package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
  162. package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
  163. package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
  164. package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
  165. package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
  166. package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
  167. package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
  168. package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
  169. package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
  170. package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
  171. package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
  172. package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
  173. package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
  174. package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
  175. package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
  176. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
  177. package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
  178. package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
  179. package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
  180. package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
  181. package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
  182. package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
@@ -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
@@ -0,0 +1,156 @@
1
+ # async-no-lock-await
2
+
3
+ > Never hold `Mutex`/`RwLock` across `.await`
4
+
5
+ ## Why It Matters
6
+
7
+ Holding a lock across an `.await` point can cause deadlocks and severely hurt performance. The task may be suspended while holding the lock, blocking all other tasks waiting for it - potentially indefinitely.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ use tokio::sync::Mutex;
13
+
14
+ async fn bad_update(state: &Mutex<State>) {
15
+ let mut guard = state.lock().await;
16
+
17
+ // BAD: Lock held across await!
18
+ let data = fetch_from_network().await;
19
+
20
+ guard.value = data;
21
+ } // Lock finally released
22
+
23
+ // This can deadlock or starve other tasks
24
+ ```
25
+
26
+ ## Good
27
+
28
+ ```rust
29
+ use tokio::sync::Mutex;
30
+
31
+ async fn good_update(state: &Mutex<State>) {
32
+ // Fetch data BEFORE taking the lock
33
+ let data = fetch_from_network().await;
34
+
35
+ // Lock only for the quick update
36
+ let mut guard = state.lock().await;
37
+ guard.value = data;
38
+ } // Lock released immediately
39
+
40
+ // Alternative: Clone data out, process, then update
41
+ async fn good_update_v2(state: &Mutex<State>) {
42
+ // Extract what we need
43
+ let id = {
44
+ let guard = state.lock().await;
45
+ guard.id.clone()
46
+ }; // Lock released!
47
+
48
+ // Do async work without lock
49
+ let data = fetch_by_id(id).await;
50
+
51
+ // Quick update
52
+ state.lock().await.value = data;
53
+ }
54
+ ```
55
+
56
+ ## The Problem Visualized
57
+
58
+ ```rust
59
+ // Task A:
60
+ let guard = mutex.lock().await; // Acquires lock
61
+ expensive_io().await; // Suspended, still holding lock!
62
+ // ... many milliseconds pass ...
63
+ drop(guard); // Finally releases
64
+
65
+ // Task B, C, D:
66
+ let guard = mutex.lock().await; // All blocked waiting for A!
67
+ ```
68
+
69
+ ## Patterns for Extraction
70
+
71
+ ```rust
72
+ use tokio::sync::Mutex;
73
+
74
+ // Pattern 1: Clone out, process, update
75
+ async fn pattern_clone(state: &Mutex<State>) {
76
+ let config = state.lock().await.config.clone();
77
+ let result = process_with_io(&config).await;
78
+ state.lock().await.result = result;
79
+ }
80
+
81
+ // Pattern 2: Compute closure, apply
82
+ async fn pattern_closure(state: &Mutex<State>) {
83
+ let update = compute_update().await;
84
+
85
+ state.lock().await.apply(update);
86
+ }
87
+
88
+ // Pattern 3: Message passing
89
+ async fn pattern_message(
90
+ state: &Mutex<State>,
91
+ tx: mpsc::Sender<Update>,
92
+ ) {
93
+ let update = compute_update().await;
94
+ tx.send(update).await.unwrap();
95
+ }
96
+
97
+ // Separate task handles updates
98
+ async fn state_manager(
99
+ state: Arc<Mutex<State>>,
100
+ mut rx: mpsc::Receiver<Update>,
101
+ ) {
102
+ while let Some(update) = rx.recv().await {
103
+ state.lock().await.apply(update);
104
+ }
105
+ }
106
+ ```
107
+
108
+ ## Using RwLock
109
+
110
+ ```rust
111
+ use tokio::sync::RwLock;
112
+
113
+ async fn read_heavy(state: &RwLock<State>) {
114
+ // Multiple readers OK, but still don't hold across await
115
+ let value = {
116
+ let guard = state.read().await;
117
+ guard.value.clone()
118
+ };
119
+
120
+ // Process without lock
121
+ let result = process(value).await;
122
+
123
+ // Write lock for update
124
+ state.write().await.result = result;
125
+ }
126
+ ```
127
+
128
+ ## std::sync::Mutex vs tokio::sync::Mutex
129
+
130
+ ```rust
131
+ // std::sync::Mutex: Blocks the entire thread
132
+ // - Use for quick, CPU-only operations
133
+ // - NEVER use in async code with await inside
134
+
135
+ // tokio::sync::Mutex: Async-aware, yields to runtime
136
+ // - Use in async code
137
+ // - Still don't hold across await points!
138
+
139
+ // std::sync::Mutex in async (quick operation, OK):
140
+ async fn quick_update(state: &std::sync::Mutex<State>) {
141
+ state.lock().unwrap().counter += 1; // No await, OK
142
+ }
143
+
144
+ // tokio::sync::Mutex (must use if lock scope has await):
145
+ async fn must_await_inside(state: &tokio::sync::Mutex<State>) {
146
+ let mut guard = state.lock().await;
147
+ // Only if you REALLY need the lock during async op
148
+ // (usually you don't - redesign instead)
149
+ }
150
+ ```
151
+
152
+ ## See Also
153
+
154
+ - [async-spawn-blocking](async-spawn-blocking.md) - Use spawn_blocking for CPU work
155
+ - [async-clone-before-await](async-clone-before-await.md) - Clone data before await
156
+ - [anti-lock-across-await](anti-lock-across-await.md) - Anti-pattern reference
@@ -0,0 +1,191 @@
1
+ # async-oneshot-response
2
+
3
+ > Use `oneshot` channel for request-response patterns
4
+
5
+ ## Why It Matters
6
+
7
+ When one task needs to send a request and wait for exactly one response, `oneshot` is the perfect fit. It's a single-use channel optimized for this pattern—no buffering, no clone overhead. Combined with `mpsc`, it enables clean actor-style message passing.
8
+
9
+ ## Bad
10
+
11
+ ```rust
12
+ // Using mpsc for single response - wasteful
13
+ let (tx, mut rx) = mpsc::channel::<Response>(1);
14
+ send_request().await;
15
+ let response = rx.recv().await.unwrap();
16
+ // Channel persists, could accidentally receive more
17
+
18
+ // Using shared state - complex
19
+ let result = Arc::new(Mutex::new(None));
20
+ send_request(result.clone()).await;
21
+ while result.lock().await.is_none() {
22
+ tokio::time::sleep(Duration::from_millis(10)).await; // Polling!
23
+ }
24
+ ```
25
+
26
+ ## Good
27
+
28
+ ```rust
29
+ use tokio::sync::oneshot;
30
+
31
+ let (tx, rx) = oneshot::channel::<Response>();
32
+
33
+ // Send request with reply channel
34
+ send_request(Request { data, reply: tx }).await;
35
+
36
+ // Wait for response
37
+ let response = rx.await?;
38
+
39
+ // Channel is consumed - can't accidentally reuse
40
+ ```
41
+
42
+ ## Request-Response Pattern
43
+
44
+ ```rust
45
+ use tokio::sync::{mpsc, oneshot};
46
+
47
+ enum Request {
48
+ Get {
49
+ key: String,
50
+ reply: oneshot::Sender<Option<Value>>,
51
+ },
52
+ Set {
53
+ key: String,
54
+ value: Value,
55
+ reply: oneshot::Sender<bool>,
56
+ },
57
+ }
58
+
59
+ // Service handler
60
+ async fn service(mut rx: mpsc::Receiver<Request>) {
61
+ let mut store = HashMap::new();
62
+
63
+ while let Some(req) = rx.recv().await {
64
+ match req {
65
+ Request::Get { key, reply } => {
66
+ let value = store.get(&key).cloned();
67
+ let _ = reply.send(value); // Ignore if receiver dropped
68
+ }
69
+ Request::Set { key, value, reply } => {
70
+ store.insert(key, value);
71
+ let _ = reply.send(true);
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ // Client
78
+ async fn get_value(tx: &mpsc::Sender<Request>, key: &str) -> Option<Value> {
79
+ let (reply_tx, reply_rx) = oneshot::channel();
80
+
81
+ tx.send(Request::Get {
82
+ key: key.to_string(),
83
+ reply: reply_tx,
84
+ }).await.ok()?;
85
+
86
+ reply_rx.await.ok()?
87
+ }
88
+ ```
89
+
90
+ ## With Timeout
91
+
92
+ ```rust
93
+ use tokio::time::{timeout, Duration};
94
+
95
+ async fn request_with_timeout(
96
+ tx: &mpsc::Sender<Request>,
97
+ key: &str,
98
+ ) -> Result<Value, Error> {
99
+ let (reply_tx, reply_rx) = oneshot::channel();
100
+
101
+ tx.send(Request::Get {
102
+ key: key.to_string(),
103
+ reply: reply_tx,
104
+ }).await.map_err(|_| Error::ServiceDown)?;
105
+
106
+ timeout(Duration::from_secs(5), reply_rx)
107
+ .await
108
+ .map_err(|_| Error::Timeout)?
109
+ .map_err(|_| Error::ServiceDown)?
110
+ .ok_or(Error::NotFound)
111
+ }
112
+ ```
113
+
114
+ ## Error Handling
115
+
116
+ ```rust
117
+ use tokio::sync::oneshot;
118
+
119
+ let (tx, rx) = oneshot::channel::<String>();
120
+
121
+ // Sender dropped without sending
122
+ drop(tx);
123
+ match rx.await {
124
+ Ok(value) => println!("Got: {}", value),
125
+ Err(oneshot::error::RecvError { .. }) => {
126
+ println!("Sender dropped");
127
+ }
128
+ }
129
+
130
+ // Receiver dropped before send
131
+ let (tx, rx) = oneshot::channel::<String>();
132
+ drop(rx);
133
+ match tx.send("hello".to_string()) {
134
+ Ok(()) => println!("Sent"),
135
+ Err(value) => println!("Receiver dropped, value: {}", value),
136
+ }
137
+ ```
138
+
139
+ ## Closed Detection
140
+
141
+ ```rust
142
+ // Check if receiver is still waiting
143
+ let (tx, rx) = oneshot::channel::<i32>();
144
+
145
+ // In producer
146
+ if tx.is_closed() {
147
+ println!("Receiver already gone, skip expensive computation");
148
+ } else {
149
+ let result = expensive_computation();
150
+ tx.send(result).ok();
151
+ }
152
+
153
+ // Async wait for close
154
+ let tx_clone = tx.clone(); // Note: can't actually clone, just showing concept
155
+ tokio::select! {
156
+ _ = tx.closed() => println!("Receiver dropped"),
157
+ result = compute() => { tx.send(result).ok(); }
158
+ }
159
+ ```
160
+
161
+ ## Response Type Wrapper
162
+
163
+ ```rust
164
+ // Standardize request-response pattern
165
+ struct RpcRequest<Req, Res> {
166
+ request: Req,
167
+ reply: oneshot::Sender<Res>,
168
+ }
169
+
170
+ impl<Req, Res> RpcRequest<Req, Res> {
171
+ fn new(request: Req) -> (Self, oneshot::Receiver<Res>) {
172
+ let (tx, rx) = oneshot::channel();
173
+ (RpcRequest { request, reply: tx }, rx)
174
+ }
175
+
176
+ fn respond(self, response: Res) {
177
+ let _ = self.reply.send(response);
178
+ }
179
+ }
180
+
181
+ // Usage
182
+ let (req, rx) = RpcRequest::new(GetUser { id: 42 });
183
+ tx.send(req).await?;
184
+ let user = rx.await?;
185
+ ```
186
+
187
+ ## See Also
188
+
189
+ - [async-mpsc-queue](./async-mpsc-queue.md) - Pair with oneshot for request-response
190
+ - [async-bounded-channel](./async-bounded-channel.md) - Channel sizing
191
+ - [async-select-racing](./async-select-racing.md) - Timeout patterns