用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill rust-async命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
正在显示 SKILL.md
基于 SOC 职业分类
| name | rust-async |
| description | | Use when this capability is needed. |
Comprehensive guide to async programming with Tokio, futures, and concurrent patterns.
// Async function returns a Future
async fn fetch_data(url: &str) -> Result<String, Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Futures are lazy - nothing happens until awaited
let future = fetch_data("https://api.example.com");
// ... future not executed yet ...
let result = future.await; // Now it runs
// Main entry point
#[tokio::main]
async fn main() {
println!("Hello, async world!");
}
// Equivalent to:
fn main() {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async {
println!("Hello, async world!");
});
}
// Multi-threaded runtime (default)
#[tokio::main]
async fn main() { ... }
// Single-threaded runtime
#[tokio::main(flavor = "current_thread")]
async fn main() { ... }
// Custom runtime
#[tokio::main(worker_threads = 4)]
async fn main() { ... }
Run tasks concurrently:
use tokio::task;
#[tokio::main]
async fn main() {
// Spawn concurrent tasks
let handle1 = task::spawn(async {
// Task 1 work
"result1"
});
let handle2 = task::spawn(async {
// Task 2 work
"result2"
});
// Wait for both
let (result1, result2) = tokio::join!(handle1, handle2);
println!("{:?}, {:?}", result1, result2);
}
For CPU-bound work:
// Don't block async runtime with CPU work
let result = tokio::task::spawn_blocking(|| {
// CPU-intensive computation
expensive_hash_computation()
}).await?;
// Or for blocking I/O (non-async libraries)
let result = tokio::task::spawn_blocking(move || {
std::fs::read_to_string("large_file.txt")
}).await??;
| Function | Use Case | Thread Pool |
|---|---|---|
spawn | Async work | Async workers |
spawn_blocking | CPU/blocking work | Blocking pool |
spawn_local | Non-Send futures | Current thread |
block_in_place | Blocking in async context | Current thread |
use tokio::join;
async fn fetch_all() -> Result<(User, Posts, Comments), Error> {
let (user, posts, comments) = join!(
fetch_user(),
fetch_posts(),
fetch_comments()
);
Ok((user?, posts?, comments?))
}
use tokio::{select, time::{sleep, Duration}};
async fn with_timeout() -> Result<Data, Error> {
select! {
result = fetch_data() => result,
_ = sleep(Duration::from_secs(5)) => {
Err(Error::Timeout)
}
}
}
// Multiple branches
select! {
msg = rx.recv() => handle_message(msg),
_ = shutdown_signal() => break,
_ = interval.tick() => do_periodic_work(),
}
use futures::stream::{FuturesUnordered, StreamExt};
async fn process_urls(urls: Vec<String>) -> Vec<Result<String, Error>> {
let mut futures = FuturesUnordered::new();
for url in urls {
futures.push(fetch(url));
}
let mut results = Vec::new();
while let Some(result) = futures.next().await {
results.push(result);
}
results
}
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel::<String>(100);
// Spawn producer
let tx2 = tx.clone();
tokio::spawn(async move {
tx.send("Hello".to_string()).await.unwrap();
});
tokio::spawn(async move {
tx2.send("World".to_string()).await.unwrap();
});
// Receive
while let Some(msg) = rx.recv().await {
println!("Got: {}", msg);
}
}
use tokio::sync::oneshot;
async fn request_response() {
let (tx, rx) = oneshot::channel();
tokio::spawn(async move {
let result = compute().await;
tx.send(result).unwrap();
});
let result = rx.await.unwrap();
}
use tokio::sync::broadcast;
let (tx, _rx) = broadcast::channel::<Event>(100);
// Multiple receivers
let mut rx1 = tx.subscribe();
let mut rx2 = tx.subscribe();
tx.send(Event::Shutdown)?;
// Both receive the event
use tokio::sync::watch;
let (tx, rx) = watch::channel(Config::default());
// Update value
tx.send(new_config)?;
// Observers receive latest value
let current = rx.borrow().clone();
// Wait for changes
rx.changed().await?;
| Type | Producers | Consumers | Buffering |
|---|---|---|---|
mpsc | Many | One | Bounded/Unbounded |
oneshot | One | One | Single value |
broadcast | One | Many | Bounded |
watch | One | Many | Latest only |
use tokio::sync::Mutex;
use std::sync::Arc;
let data = Arc::new(Mutex::new(vec![]));
let data2 = data.clone();
tokio::spawn(async move {
let mut guard = data2.lock().await;
guard.push(1);
}); // Lock released when guard drops
// Don't hold locks across .await when possible
let item = {
let guard = data.lock().await;
guard.first().cloned()
}; // Lock released before await
process(item).await;
use tokio::sync::RwLock;
let data = RwLock::new(HashMap::new());
// Multiple readers
let reader1 = data.read().await;
let reader2 = data.read().await;
// Exclusive writer
let mut writer = data.write().await;
writer.insert("key", "value");
use tokio::sync::Semaphore;
use std::sync::Arc;
let semaphore = Arc::new(Semaphore::new(10)); // Max 10 concurrent
async fn limited_task(sem: Arc<Semaphore>) {
let permit = sem.acquire().await.unwrap();
// Do work with permit held
do_work().await;
// Permit dropped, slot freed
}
use tokio::fs;
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader, BufWriter};
// Read file
let content = fs::read_to_string("file.txt").await?;
// Write file
fs::write("output.txt", content).await?;
// Buffered I/O
let file = fs::File::open("large.txt").await?;
let mut reader = BufReader::new(file);
let mut line = String::new();
reader.read_line(&mut line).await?;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
// Server
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (socket, addr) = listener.accept().await?;
tokio::spawn(async move {
handle_connection(socket).await;
});
}
// Client
let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
stream.write_all(b"Hello").await?;
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf).await?;
use tokio::signal;
use tokio::sync::watch;
#[tokio::main]
async fn main() {
let (shutdown_tx, shutdown_rx) = watch::channel(false);
let server = tokio::spawn(run_server(shutdown_rx.clone()));
// Wait for Ctrl+C
signal::ctrl_c().await.unwrap();
// Signal shutdown
shutdown_tx.send(true).unwrap();
// Wait for server to finish
server.await.unwrap();
}
async fn run_server(mut shutdown: watch::Receiver<bool>) {
loop {
select! {
conn = accept_connection() => handle(conn).await,
_ = shutdown.changed() => {
if *shutdown.borrow() {
break;
}
}
}
}
}
use tokio::time::{interval, Duration};
async fn rate_limited_work() {
let mut interval = interval(Duration::from_millis(100));
for item in items {
interval.tick().await; // 10 per second max
process(item).await;
}
}
use tokio::time::{timeout, Duration};
let result = timeout(
Duration::from_secs(5),
fetch_data()
).await;
match result {
Ok(data) => println!("Got data: {:?}", data),
Err(_) => println!("Timed out"),
}
use tokio::time::{sleep, Duration};
async fn retry_with_backoff<F, Fut, T, E>(
mut f: F,
max_retries: u32,
) -> Result<T, E>
where
F: FnMut() -> Fut,
Fut: Future<Output = Result<T, E>>,
{
let mut delay = Duration::from_millis(100);
for attempt in 0..max_retries {
match f().await {
Ok(result) => return Ok(result),
Err(e) if attempt + 1 == max_retries => return Err(e),
Err(_) => {
sleep(delay).await;
delay *= 2; // Exponential backoff
}
}
}
unreachable!()
}
Manage a dynamic set of tasks with automatic cleanup:
use tokio::task::JoinSet;
async fn process_all(items: Vec<Item>) -> Vec<Result<Output, Error>> {
let mut set = JoinSet::new();
for item in items {
set.spawn(async move { process(item).await });
}
let mut results = Vec::new();
while let Some(result) = set.join_next().await {
results.push(result.unwrap()); // unwrap JoinError (panic propagation)
}
results
}
// With concurrency limit
async fn limited_parallel(items: Vec<Item>, limit: usize) {
let mut set = JoinSet::new();
for item in items {
// Keep at most `limit` tasks running
if set.len() >= limit {
set.().;
}
set.( { (item). });
}
set.()..() {}
}
use tokio_util::sync::CancellationToken;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let token = CancellationToken::new();
// Worker task respects cancellation
let worker_token = token.clone();
let worker = tokio::spawn(async move {
loop {
tokio::select! {
_ = worker_token.cancelled() => {
println!("Worker shutting down cleanly");
break;
}
_ = do_work() => {}
}
}
});
// Let it run for 2 seconds, then cancel
sleep(Duration::from_secs(2)).await;
token.cancel(); // Notifies all clones
worker.await.unwrap();
}
// Dropping handle vs cancelling:
let handle = tokio::spawn(async { loop { work().await; } });
drop(handle); // Task CONTINUES in background (detached)!
token.();
use tokio::time::{timeout, Duration};
// Task is dropped (cancelled) if it doesn't finish in time
match timeout(Duration::from_secs(5), long_running_task()).await {
Ok(result) => println!("Completed: {:?}", result),
Err(_elapsed) => println!("Timed out — task was cancelled"),
}
// BAD: Lock held across await
let guard = mutex.lock().await;
some_async_operation().await; // Lock still held!
drop(guard);
// GOOD: Clone and release
let data = {
let guard = mutex.lock().await;
guard.clone()
};
some_async_operation_with(data).await;
// BAD: Blocks the async runtime
async fn bad() {
std::thread::sleep(Duration::from_secs(1)); // Blocks!
std::fs::read_to_string("file.txt"); // Blocks!
}
// GOOD: Use async equivalents
async fn good() {
tokio::time::sleep(Duration::from_secs(1)).await;
tokio::fs::read_to_string("file.txt").await;
}
// Or spawn_blocking for unavoidable blocking
let result = tokio::task::spawn_blocking(|| {
blocking_library_call()
}).await?;
// BAD: Nested runtime
async fn bad() {
tokio::runtime::Runtime::new().unwrap()
.block_on(async { ... }); // Panic or deadlock!
}
// GOOD: Just await
async fn good() {
some_future().await;
}
Avoid unbounded channels (like tokio::sync::mpsc::unbounded_channel) in production pipelines. Without backpressure, slow consumers will cause memory leaks. Always specify a bound (capacity):
// GOOD: Capacity is capped; send will block cooperatively or fail when full
let (tx, mut rx) = tokio::sync::mpsc::channel(100);
Instead of collecting handles and iterating or using unstable futures::future::join_all, use tokio::task::JoinSet to manage lifetimes of dynamically spawned concurrent workers.
use tokio::task::JoinSet;
let mut set = JoinSet::new();
for i in 0..10 {
set.spawn(async move { i * 2 });
}
while let Some(res) = set.join_next().await {
println!("Task finished: {:?}", res?);
}
#[tokio::test] for async teststracing cratetokio-console for runtime inspection (cargo install tokio-console).await)CancellationToken over Arc<AtomicBool> — atomic flags don't wake sleeping tasksJoinSet or structured task ownership for dynamic task groups.spawn_blocking.tracing spans and inspect with tokio-console when needed.Source: adxptived/Rust-Skills — distributed by TomeVault.