agy-superpowers 5.1.4 → 5.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/agent/rules/debug-confirmation-policy.md +34 -0
- package/template/agent/rules/language-matching.md +32 -0
- package/template/agent/skills/rust-developer/SKILL.md +281 -0
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# async-tokio-fs
|
|
2
|
+
|
|
3
|
+
> Use `tokio::fs` instead of `std::fs` in async code
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`std::fs` operations are blocking—they stop the current thread until the syscall completes. In async code, this blocks the executor thread, preventing it from running other tasks. `tokio::fs` wraps filesystem operations in `spawn_blocking`, keeping the executor responsive.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
async fn process_files(paths: &[PathBuf]) -> Result<Vec<String>> {
|
|
13
|
+
let mut contents = Vec::new();
|
|
14
|
+
|
|
15
|
+
for path in paths {
|
|
16
|
+
// BLOCKS the entire executor thread!
|
|
17
|
+
let data = std::fs::read_to_string(path)?;
|
|
18
|
+
contents.push(data);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Ok(contents)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// While reading a file, NO other tasks can run on this thread
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Good
|
|
28
|
+
|
|
29
|
+
```rust
|
|
30
|
+
use tokio::fs;
|
|
31
|
+
|
|
32
|
+
async fn process_files(paths: &[PathBuf]) -> Result<Vec<String>> {
|
|
33
|
+
let mut contents = Vec::new();
|
|
34
|
+
|
|
35
|
+
for path in paths {
|
|
36
|
+
// Non-blocking: allows other tasks to run
|
|
37
|
+
let data = fs::read_to_string(path).await?;
|
|
38
|
+
contents.push(data);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Ok(contents)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Even better: concurrent reads
|
|
45
|
+
async fn process_files_concurrent(paths: &[PathBuf]) -> Result<Vec<String>> {
|
|
46
|
+
let futures: Vec<_> = paths.iter()
|
|
47
|
+
.map(|path| fs::read_to_string(path))
|
|
48
|
+
.collect();
|
|
49
|
+
|
|
50
|
+
futures::future::try_join_all(futures).await
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## tokio::fs API
|
|
55
|
+
|
|
56
|
+
```rust
|
|
57
|
+
use tokio::fs;
|
|
58
|
+
|
|
59
|
+
// Reading
|
|
60
|
+
let contents = fs::read_to_string("file.txt").await?;
|
|
61
|
+
let bytes = fs::read("file.bin").await?;
|
|
62
|
+
|
|
63
|
+
// Writing
|
|
64
|
+
fs::write("output.txt", "contents").await?;
|
|
65
|
+
|
|
66
|
+
// File operations
|
|
67
|
+
let file = fs::File::open("file.txt").await?;
|
|
68
|
+
let file = fs::File::create("new.txt").await?;
|
|
69
|
+
|
|
70
|
+
// Directory operations
|
|
71
|
+
fs::create_dir("new_dir").await?;
|
|
72
|
+
fs::create_dir_all("nested/dir/path").await?;
|
|
73
|
+
fs::remove_dir("empty_dir").await?;
|
|
74
|
+
fs::remove_dir_all("dir_with_contents").await?;
|
|
75
|
+
|
|
76
|
+
// Metadata
|
|
77
|
+
let metadata = fs::metadata("file.txt").await?;
|
|
78
|
+
let canonical = fs::canonicalize("./relative").await?;
|
|
79
|
+
|
|
80
|
+
// Rename/remove
|
|
81
|
+
fs::rename("old.txt", "new.txt").await?;
|
|
82
|
+
fs::remove_file("file.txt").await?;
|
|
83
|
+
|
|
84
|
+
// Read directory
|
|
85
|
+
let mut entries = fs::read_dir("some_dir").await?;
|
|
86
|
+
while let Some(entry) = entries.next_entry().await? {
|
|
87
|
+
println!("{}", entry.path().display());
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Async File I/O
|
|
92
|
+
|
|
93
|
+
```rust
|
|
94
|
+
use tokio::fs::File;
|
|
95
|
+
use tokio::io::{AsyncReadExt, AsyncWriteExt, AsyncBufReadExt, BufReader};
|
|
96
|
+
|
|
97
|
+
// Read with buffer
|
|
98
|
+
let mut file = File::open("large.bin").await?;
|
|
99
|
+
let mut buffer = vec![0u8; 4096];
|
|
100
|
+
let bytes_read = file.read(&mut buffer).await?;
|
|
101
|
+
|
|
102
|
+
// Read all
|
|
103
|
+
let mut contents = Vec::new();
|
|
104
|
+
file.read_to_end(&mut contents).await?;
|
|
105
|
+
|
|
106
|
+
// Write
|
|
107
|
+
let mut file = File::create("output.bin").await?;
|
|
108
|
+
file.write_all(b"data").await?;
|
|
109
|
+
file.flush().await?;
|
|
110
|
+
|
|
111
|
+
// Buffered line reading
|
|
112
|
+
let file = File::open("lines.txt").await?;
|
|
113
|
+
let reader = BufReader::new(file);
|
|
114
|
+
let mut lines = reader.lines();
|
|
115
|
+
|
|
116
|
+
while let Some(line) = lines.next_line().await? {
|
|
117
|
+
println!("{}", line);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## When std::fs is Acceptable
|
|
122
|
+
|
|
123
|
+
```rust
|
|
124
|
+
// Startup/initialization (before async runtime)
|
|
125
|
+
fn main() {
|
|
126
|
+
let config = std::fs::read_to_string("config.toml")
|
|
127
|
+
.expect("config file required");
|
|
128
|
+
|
|
129
|
+
tokio::runtime::Runtime::new()
|
|
130
|
+
.unwrap()
|
|
131
|
+
.block_on(run_with_config(config));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Single-threaded current_thread runtime (less impact)
|
|
135
|
+
#[tokio::main(flavor = "current_thread")]
|
|
136
|
+
async fn main() {
|
|
137
|
+
// Still prefer tokio::fs, but impact is lower
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// When file operations are rare and quick
|
|
141
|
+
// (e.g., reading small config once per hour)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Performance Considerations
|
|
145
|
+
|
|
146
|
+
```rust
|
|
147
|
+
// tokio::fs uses spawn_blocking internally
|
|
148
|
+
// For many small files, the overhead adds up
|
|
149
|
+
|
|
150
|
+
// Batch operations when possible
|
|
151
|
+
let paths: Vec<_> = entries.iter()
|
|
152
|
+
.map(|e| e.path())
|
|
153
|
+
.collect();
|
|
154
|
+
|
|
155
|
+
let contents = futures::future::try_join_all(
|
|
156
|
+
paths.iter().map(|p| fs::read_to_string(p))
|
|
157
|
+
).await?;
|
|
158
|
+
|
|
159
|
+
// For heavy I/O, consider memory-mapped files
|
|
160
|
+
// (requires unsafe or mmap crate)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## See Also
|
|
164
|
+
|
|
165
|
+
- [async-spawn-blocking](./async-spawn-blocking.md) - How tokio::fs works internally
|
|
166
|
+
- [async-tokio-runtime](./async-tokio-runtime.md) - Runtime configuration
|
|
167
|
+
- [err-context-chain](./err-context-chain.md) - Adding path context to IO errors
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# async-tokio-runtime
|
|
2
|
+
|
|
3
|
+
> Configure Tokio runtime appropriately for your workload
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Tokio's default multi-threaded runtime isn't always optimal. CPU-bound work needs different configuration than IO-bound work. Incorrect configuration leads to poor performance, blocked workers, or resource exhaustion. Understanding runtime options lets you tune for your specific use case.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Default runtime for everything - not optimal
|
|
13
|
+
#[tokio::main]
|
|
14
|
+
async fn main() {
|
|
15
|
+
// CPU-heavy work on async executor starves IO tasks
|
|
16
|
+
for data in datasets {
|
|
17
|
+
let result = heavy_computation(data).await;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Single-threaded when multi-threaded is needed
|
|
22
|
+
#[tokio::main(flavor = "current_thread")]
|
|
23
|
+
async fn main() {
|
|
24
|
+
// Can't utilize multiple cores for concurrent tasks
|
|
25
|
+
for _ in 0..1000 {
|
|
26
|
+
tokio::spawn(async { /* IO work */ });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Good
|
|
32
|
+
|
|
33
|
+
```rust
|
|
34
|
+
// Multi-threaded for concurrent IO (default)
|
|
35
|
+
#[tokio::main]
|
|
36
|
+
async fn main() {
|
|
37
|
+
// Good for many concurrent network connections
|
|
38
|
+
let handles: Vec<_> = urls.iter()
|
|
39
|
+
.map(|url| tokio::spawn(fetch(url.clone())))
|
|
40
|
+
.collect();
|
|
41
|
+
|
|
42
|
+
futures::future::join_all(handles).await;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Current-thread for single-threaded scenarios
|
|
46
|
+
#[tokio::main(flavor = "current_thread")]
|
|
47
|
+
async fn main() {
|
|
48
|
+
// Good for single-connection clients, simpler debugging
|
|
49
|
+
let client = Client::new();
|
|
50
|
+
client.run().await;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Custom configuration
|
|
54
|
+
#[tokio::main(worker_threads = 4)]
|
|
55
|
+
async fn main() {
|
|
56
|
+
// Limit to 4 worker threads
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Or manual setup for more control
|
|
60
|
+
fn main() {
|
|
61
|
+
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
62
|
+
.worker_threads(4)
|
|
63
|
+
.enable_all()
|
|
64
|
+
.thread_name("my-worker")
|
|
65
|
+
.build()
|
|
66
|
+
.unwrap();
|
|
67
|
+
|
|
68
|
+
runtime.block_on(async_main());
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Runtime Types
|
|
73
|
+
|
|
74
|
+
| Runtime | Use Case | Configuration |
|
|
75
|
+
|---------|----------|---------------|
|
|
76
|
+
| Multi-thread | IO-bound, many connections | `#[tokio::main]` (default) |
|
|
77
|
+
| Current-thread | CLI tools, tests, single connection | `flavor = "current_thread"` |
|
|
78
|
+
| Custom | Fine-tuned performance | `Builder::new_*()` |
|
|
79
|
+
|
|
80
|
+
## Worker Thread Tuning
|
|
81
|
+
|
|
82
|
+
```rust
|
|
83
|
+
use tokio::runtime::Builder;
|
|
84
|
+
|
|
85
|
+
// IO-bound: more threads than cores can help
|
|
86
|
+
let io_runtime = Builder::new_multi_thread()
|
|
87
|
+
.worker_threads(num_cpus::get() * 2) // IO can benefit from oversubscription
|
|
88
|
+
.max_blocking_threads(32) // For spawn_blocking calls
|
|
89
|
+
.enable_io()
|
|
90
|
+
.enable_time()
|
|
91
|
+
.build()?;
|
|
92
|
+
|
|
93
|
+
// CPU-bound: match core count
|
|
94
|
+
let cpu_runtime = Builder::new_multi_thread()
|
|
95
|
+
.worker_threads(num_cpus::get()) // No benefit from more than cores
|
|
96
|
+
.build()?;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Multiple Runtimes
|
|
100
|
+
|
|
101
|
+
```rust
|
|
102
|
+
// Separate runtimes for different workloads
|
|
103
|
+
struct App {
|
|
104
|
+
io_runtime: Runtime,
|
|
105
|
+
cpu_runtime: Runtime,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
impl App {
|
|
109
|
+
fn new() -> Self {
|
|
110
|
+
Self {
|
|
111
|
+
io_runtime: Builder::new_multi_thread()
|
|
112
|
+
.worker_threads(8)
|
|
113
|
+
.thread_name("io-worker")
|
|
114
|
+
.build()
|
|
115
|
+
.unwrap(),
|
|
116
|
+
cpu_runtime: Builder::new_multi_thread()
|
|
117
|
+
.worker_threads(4)
|
|
118
|
+
.thread_name("cpu-worker")
|
|
119
|
+
.build()
|
|
120
|
+
.unwrap(),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
fn spawn_io<F>(&self, future: F)
|
|
125
|
+
where F: Future + Send + 'static, F::Output: Send + 'static
|
|
126
|
+
{
|
|
127
|
+
self.io_runtime.spawn(future);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn spawn_cpu<F>(&self, task: F)
|
|
131
|
+
where F: FnOnce() + Send + 'static
|
|
132
|
+
{
|
|
133
|
+
self.cpu_runtime.spawn_blocking(task);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Runtime in Tests
|
|
139
|
+
|
|
140
|
+
```rust
|
|
141
|
+
// Single test runtime
|
|
142
|
+
#[tokio::test]
|
|
143
|
+
async fn test_single() {
|
|
144
|
+
assert!(true);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Multi-threaded test
|
|
148
|
+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
149
|
+
async fn test_concurrent() {
|
|
150
|
+
let (tx, rx) = tokio::sync::oneshot::channel();
|
|
151
|
+
tokio::spawn(async move { tx.send(42).unwrap() });
|
|
152
|
+
assert_eq!(rx.await.unwrap(), 42);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Custom runtime in test
|
|
156
|
+
#[test]
|
|
157
|
+
fn test_with_custom_runtime() {
|
|
158
|
+
let rt = Builder::new_current_thread().build().unwrap();
|
|
159
|
+
rt.block_on(async {
|
|
160
|
+
// test code
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## See Also
|
|
166
|
+
|
|
167
|
+
- [async-spawn-blocking](./async-spawn-blocking.md) - Handling blocking code
|
|
168
|
+
- [async-no-lock-await](./async-no-lock-await.md) - Avoiding lock issues
|
|
169
|
+
- [async-joinset-structured](./async-joinset-structured.md) - Managing spawned tasks
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# async-try-join
|
|
2
|
+
|
|
3
|
+
> Use `try_join!` for concurrent fallible operations with early return on error
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
When running multiple fallible operations concurrently, `try_join!` returns `Err` as soon as any future fails, without waiting for the others. This provides fail-fast behavior while still running operations in parallel. For many operations, use `futures::future::try_join_all`.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Sequential - slow and no early return benefit
|
|
13
|
+
async fn fetch_all() -> Result<(A, B, C)> {
|
|
14
|
+
let a = fetch_a().await?; // If this fails, we wait for nothing
|
|
15
|
+
let b = fetch_b().await?; // But if this fails, we waited for A
|
|
16
|
+
let c = fetch_c().await?;
|
|
17
|
+
Ok((a, b, c))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// join! ignores errors
|
|
21
|
+
async fn fetch_all() -> (Result<A>, Result<B>, Result<C>) {
|
|
22
|
+
let (a, b, c) = join!(fetch_a(), fetch_b(), fetch_c());
|
|
23
|
+
// All complete even if first one failed
|
|
24
|
+
(a, b, c) // Now we have to handle three Results
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Good
|
|
29
|
+
|
|
30
|
+
```rust
|
|
31
|
+
use tokio::try_join;
|
|
32
|
+
|
|
33
|
+
async fn fetch_all() -> Result<(A, B, C)> {
|
|
34
|
+
// Concurrent AND fail-fast
|
|
35
|
+
let (a, b, c) = try_join!(
|
|
36
|
+
fetch_a(),
|
|
37
|
+
fetch_b(),
|
|
38
|
+
fetch_c(),
|
|
39
|
+
)?;
|
|
40
|
+
|
|
41
|
+
Ok((a, b, c))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// For dynamic collections
|
|
45
|
+
use futures::future::try_join_all;
|
|
46
|
+
|
|
47
|
+
async fn fetch_users(ids: &[u64]) -> Result<Vec<User>> {
|
|
48
|
+
let futures: Vec<_> = ids.iter()
|
|
49
|
+
.map(|id| fetch_user(*id))
|
|
50
|
+
.collect();
|
|
51
|
+
|
|
52
|
+
try_join_all(futures).await
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Error Handling Patterns
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
// Different error types - need common error type
|
|
60
|
+
async fn mixed_operations() -> Result<(A, B), Error> {
|
|
61
|
+
let (a, b) = try_join!(
|
|
62
|
+
fetch_a().map_err(Error::from), // Convert errors
|
|
63
|
+
fetch_b().map_err(Error::from),
|
|
64
|
+
)?;
|
|
65
|
+
Ok((a, b))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Collect all results, then handle errors
|
|
69
|
+
async fn all_or_nothing(ids: &[u64]) -> Result<Vec<User>> {
|
|
70
|
+
try_join_all(ids.iter().map(|id| fetch_user(*id))).await
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Collect successes, log failures
|
|
74
|
+
async fn best_effort(ids: &[u64]) -> Vec<User> {
|
|
75
|
+
let results = futures::future::join_all(
|
|
76
|
+
ids.iter().map(|id| fetch_user(*id))
|
|
77
|
+
).await;
|
|
78
|
+
|
|
79
|
+
results.into_iter()
|
|
80
|
+
.filter_map(|r| match r {
|
|
81
|
+
Ok(user) => Some(user),
|
|
82
|
+
Err(e) => {
|
|
83
|
+
log::warn!("Failed to fetch user: {}", e);
|
|
84
|
+
None
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
.collect()
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Cancellation Behavior
|
|
92
|
+
|
|
93
|
+
```rust
|
|
94
|
+
// try_join! cancels remaining futures on error
|
|
95
|
+
async fn with_cancellation() -> Result<()> {
|
|
96
|
+
// If fetch_a() fails, fetch_b() and fetch_c() are dropped
|
|
97
|
+
// But "dropped" != "immediately stopped"
|
|
98
|
+
// They stop at their next .await point
|
|
99
|
+
|
|
100
|
+
try_join!(
|
|
101
|
+
async {
|
|
102
|
+
fetch_a().await?;
|
|
103
|
+
cleanup_a().await; // May not run if other future fails
|
|
104
|
+
Ok::<_, Error>(())
|
|
105
|
+
},
|
|
106
|
+
async {
|
|
107
|
+
fetch_b().await?;
|
|
108
|
+
cleanup_b().await; // May not run if other future fails
|
|
109
|
+
Ok::<_, Error>(())
|
|
110
|
+
},
|
|
111
|
+
)?;
|
|
112
|
+
|
|
113
|
+
Ok(())
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// For guaranteed cleanup, use Drop guards or explicit handling
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## With Timeout
|
|
120
|
+
|
|
121
|
+
```rust
|
|
122
|
+
use tokio::time::{timeout, Duration};
|
|
123
|
+
|
|
124
|
+
async fn fetch_with_timeout() -> Result<(A, B)> {
|
|
125
|
+
timeout(
|
|
126
|
+
Duration::from_secs(10),
|
|
127
|
+
try_join!(fetch_a(), fetch_b())
|
|
128
|
+
)
|
|
129
|
+
.await
|
|
130
|
+
.map_err(|_| Error::Timeout)?
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Per-operation timeout
|
|
134
|
+
async fn individual_timeouts() -> Result<(A, B)> {
|
|
135
|
+
try_join!(
|
|
136
|
+
timeout(Duration::from_secs(5), fetch_a())
|
|
137
|
+
.map_err(|_| Error::Timeout)
|
|
138
|
+
.and_then(|r| async { r }),
|
|
139
|
+
timeout(Duration::from_secs(5), fetch_b())
|
|
140
|
+
.map_err(|_| Error::Timeout)
|
|
141
|
+
.and_then(|r| async { r }),
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## try_join! vs FuturesUnordered
|
|
147
|
+
|
|
148
|
+
```rust
|
|
149
|
+
use futures::stream::{FuturesUnordered, StreamExt};
|
|
150
|
+
|
|
151
|
+
// try_join!: wait for all, fail fast
|
|
152
|
+
let (a, b, c) = try_join!(fa, fb, fc)?;
|
|
153
|
+
|
|
154
|
+
// FuturesUnordered: process as they complete
|
|
155
|
+
let mut futures = FuturesUnordered::new();
|
|
156
|
+
futures.push(fetch_a());
|
|
157
|
+
futures.push(fetch_b());
|
|
158
|
+
futures.push(fetch_c());
|
|
159
|
+
|
|
160
|
+
while let Some(result) = futures.next().await {
|
|
161
|
+
match result {
|
|
162
|
+
Ok(data) => process(data),
|
|
163
|
+
Err(e) => return Err(e), // Can fail fast manually
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## See Also
|
|
169
|
+
|
|
170
|
+
- [async-join-parallel](./async-join-parallel.md) - Non-fallible concurrent futures
|
|
171
|
+
- [async-select-racing](./async-select-racing.md) - First-to-complete semantics
|
|
172
|
+
- [err-question-mark](./err-question-mark.md) - Error propagation
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# async-watch-latest
|
|
2
|
+
|
|
3
|
+
> Use `watch` channel for sharing the latest value with multiple observers
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`watch` is optimized for scenarios where receivers only care about the most recent value, not the history of changes. Unlike `broadcast`, slow receivers don't lag—they simply skip intermediate values. This is perfect for configuration, state, or status that should always reflect the current situation.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Using broadcast when only latest value matters
|
|
13
|
+
let (tx, _) = broadcast::channel::<Config>(100);
|
|
14
|
+
|
|
15
|
+
// Receivers might process stale configs if they're slow
|
|
16
|
+
// And they waste time processing intermediate values
|
|
17
|
+
|
|
18
|
+
// Using mpsc with buffered stale values
|
|
19
|
+
let (tx, mut rx) = mpsc::channel::<Status>(100);
|
|
20
|
+
// Receiver might process outdated statuses
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Good
|
|
24
|
+
|
|
25
|
+
```rust
|
|
26
|
+
use tokio::sync::watch;
|
|
27
|
+
|
|
28
|
+
let (tx, rx) = watch::channel(Config::default());
|
|
29
|
+
|
|
30
|
+
// Multiple observers
|
|
31
|
+
let rx1 = rx.clone();
|
|
32
|
+
let rx2 = rx.clone();
|
|
33
|
+
|
|
34
|
+
// Observer 1: waits for changes
|
|
35
|
+
tokio::spawn(async move {
|
|
36
|
+
let mut rx = rx1;
|
|
37
|
+
while rx.changed().await.is_ok() {
|
|
38
|
+
let config = rx.borrow();
|
|
39
|
+
apply_config(&*config);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Observer 2: also sees all changes
|
|
44
|
+
tokio::spawn(async move {
|
|
45
|
+
let mut rx = rx2;
|
|
46
|
+
while rx.changed().await.is_ok() {
|
|
47
|
+
let config = rx.borrow();
|
|
48
|
+
log_config_change(&*config);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Update the value
|
|
53
|
+
tx.send(Config::new())?;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## watch Semantics
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
use tokio::sync::watch;
|
|
60
|
+
|
|
61
|
+
let (tx, mut rx) = watch::channel("initial");
|
|
62
|
+
|
|
63
|
+
// Immediate read - no waiting
|
|
64
|
+
assert_eq!(*rx.borrow(), "initial");
|
|
65
|
+
|
|
66
|
+
// Wait for change
|
|
67
|
+
tx.send("updated")?;
|
|
68
|
+
rx.changed().await?;
|
|
69
|
+
assert_eq!(*rx.borrow(), "updated");
|
|
70
|
+
|
|
71
|
+
// Multiple rapid updates - receiver sees latest
|
|
72
|
+
tx.send("v1")?;
|
|
73
|
+
tx.send("v2")?;
|
|
74
|
+
tx.send("v3")?;
|
|
75
|
+
rx.changed().await?;
|
|
76
|
+
assert_eq!(*rx.borrow(), "v3"); // Skipped v1, v2
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Configuration Reload Pattern
|
|
80
|
+
|
|
81
|
+
```rust
|
|
82
|
+
use tokio::sync::watch;
|
|
83
|
+
use std::sync::Arc;
|
|
84
|
+
|
|
85
|
+
struct AppConfig {
|
|
86
|
+
log_level: Level,
|
|
87
|
+
max_connections: usize,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async fn config_watcher(tx: watch::Sender<Arc<AppConfig>>) {
|
|
91
|
+
loop {
|
|
92
|
+
tokio::time::sleep(Duration::from_secs(60)).await;
|
|
93
|
+
|
|
94
|
+
if let Ok(new_config) = reload_config_from_disk() {
|
|
95
|
+
// Only notifies if value actually changed
|
|
96
|
+
tx.send_if_modified(|current| {
|
|
97
|
+
if *current != new_config {
|
|
98
|
+
*current = Arc::new(new_config);
|
|
99
|
+
true
|
|
100
|
+
} else {
|
|
101
|
+
false
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async fn worker(mut config_rx: watch::Receiver<Arc<AppConfig>>) {
|
|
109
|
+
loop {
|
|
110
|
+
tokio::select! {
|
|
111
|
+
_ = config_rx.changed() => {
|
|
112
|
+
let config = config_rx.borrow().clone();
|
|
113
|
+
reconfigure(&config);
|
|
114
|
+
}
|
|
115
|
+
_ = do_work() => {}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## State Machine Updates
|
|
122
|
+
|
|
123
|
+
```rust
|
|
124
|
+
#[derive(Clone, PartialEq)]
|
|
125
|
+
enum ConnectionState {
|
|
126
|
+
Disconnected,
|
|
127
|
+
Connecting,
|
|
128
|
+
Connected,
|
|
129
|
+
Error(String),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
struct Connection {
|
|
133
|
+
state_tx: watch::Sender<ConnectionState>,
|
|
134
|
+
state_rx: watch::Receiver<ConnectionState>,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
impl Connection {
|
|
138
|
+
async fn wait_connected(&mut self) -> Result<(), Error> {
|
|
139
|
+
loop {
|
|
140
|
+
let state = self.state_rx.borrow().clone();
|
|
141
|
+
match state {
|
|
142
|
+
ConnectionState::Connected => return Ok(()),
|
|
143
|
+
ConnectionState::Error(e) => return Err(Error::Connection(e)),
|
|
144
|
+
_ => {
|
|
145
|
+
self.state_rx.changed().await?;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Borrow vs Clone
|
|
154
|
+
|
|
155
|
+
```rust
|
|
156
|
+
use tokio::sync::watch;
|
|
157
|
+
|
|
158
|
+
let (tx, rx) = watch::channel(vec![1, 2, 3]);
|
|
159
|
+
|
|
160
|
+
// borrow() returns Ref - must not hold across await
|
|
161
|
+
{
|
|
162
|
+
let data = rx.borrow();
|
|
163
|
+
println!("{:?}", *data);
|
|
164
|
+
} // Ref dropped here
|
|
165
|
+
|
|
166
|
+
// For use across await, clone the data
|
|
167
|
+
let data = rx.borrow().clone();
|
|
168
|
+
some_async_operation().await;
|
|
169
|
+
use_data(&data); // Safe
|
|
170
|
+
|
|
171
|
+
// Or use borrow_and_update() to mark as seen
|
|
172
|
+
let data = rx.borrow_and_update().clone();
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## watch vs broadcast vs mpsc
|
|
176
|
+
|
|
177
|
+
| Feature | watch | broadcast | mpsc |
|
|
178
|
+
|---------|-------|-----------|------|
|
|
179
|
+
| Receivers | Multiple | Multiple | Single |
|
|
180
|
+
| Message delivery | Latest only | All messages | All messages |
|
|
181
|
+
| Slow receiver | Skips to latest | Lags/misses | Backpressure |
|
|
182
|
+
| Clone required | No | Yes | No |
|
|
183
|
+
| Best for | Config, status | Events | Work queues |
|
|
184
|
+
|
|
185
|
+
## See Also
|
|
186
|
+
|
|
187
|
+
- [async-broadcast-pubsub](./async-broadcast-pubsub.md) - When history matters
|
|
188
|
+
- [async-mpsc-queue](./async-mpsc-queue.md) - Work queue patterns
|
|
189
|
+
- [async-cancellation-token](./async-cancellation-token.md) - Related pattern
|