К основному содержанию
скилл CX Codex

rust-async-internals — скилл для Claude Code: разбор async в Rust

Помогает агенту объяснять и отлаживать async Rust: модель poll, Pin и Unpin, планировщик tokio, утечки waker. Пригодится, когда задача виснет или тормозит.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add mohitmishra786/low-level-dev-skills --skill rust-async-internals -a claude-code
Открыть на GitHub

Что делает rust-async-internals

Скилл подключается, когда в разговоре всплывают внутренности async Rust: трейт Future и цикл опроса, Pin/Unpin для самоссылающихся типов, модель задач tokio, диагностика асинхронных стеков через tokio-console, поиск утечек waker, подводные камни select! и join!. Агент получает готовую схему объяснения, а не общие слова про async/await.

Внутри — рабочий порядок разбора. Сначала модель poll: что возвращают Poll::Ready и Poll::Pending, зачем нужен waker. Потом пример своей реализации Future для задержки. Дальше Pin и Unpin с объяснением, почему конечный автомат из async fn нельзя двигать после приостановки. Отдельные разделы про tokio::spawn, spawn_blocking, LocalSet и про tokio-console: зависимости в Cargo.toml, вызов console_subscriber::init() до старта рантайма, запуск с RUSTFLAGS="--cfg tokio_unstable".

Есть раздел про блокирующие вызовы в async-контексте: std::thread::sleep и std::fs останавливают поток рантайма, поэтому нужны tokio::time::sleep, tokio::fs или spawn_blocking. Для tokio-console понадобится установленный CLI: cargo install --locked tokio-console. Учтите, что ветки select!, которые не выиграли, отбрасываются сразу — это повод проверить, не держит ли отменённая ветка нужное состояние.

Какие задачи решает

  • понять, как на самом деле работает цикл опроса Future
  • объяснить, зачем нужны Pin и Unpin в async-коде
  • найти блокирующий вызов, который тормозит поток рантайма
  • разобрать асинхронный стек через tokio-console
  • отследить утечку waker в своей задаче

Примеры запросов агенту

  • Объясни, как устроен poll у Future в Rust и когда возвращается Pending
  • У меня блокирующий вызов внутри async-функции — покажи, как переписать через spawn_blocking
  • Помоги подключить tokio-console и найти задачу, которая долго висит в Pending

Как установить rust-async-internals

1Через Skills CLI (Vercel)

npx skills add mohitmishra786/low-level-dev-skills --skill rust-async-internals -a claude-code

2Вручную: положить файлы

~/.claude/skills/rust-async-internals/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/rust-async-internals/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора62

include-what-you-use Скилл ведёт агента через IWYU: разбирает отчёты, применяет mapping-файлы и убирает лишние #include, из-за которых сборка C++ расползается. CX Codex +1 480установок rust-no-std Скилл для разработки на Rust без стандартной библиотеки: разбирает границу core, alloc и std, глобальные аллокаторы и обработчики паник. CX Codex +1 474установок dwarf-debug-format Скилл объясняет агенту, как читать отладочную информацию DWARF: секции ELF, split DWARF, debuginfod, влияние LTO и stripping. CX Codex +1 471установок intel-vtune-amd-uprof Скилл для профилирования процессора: помогает найти горячие функции, разобрать промахи кэша и заторы конвейера через Intel VTune и AMD uProf. CX Codex +1 469установок zig-compiler Ведёт агента по вызову компилятора Zig: режимы оптимизации, типы сборки, zig cc вместо C-компилятора и разбор сообщений об ошибках. CX Codex +1 450установок zig-comptime Скилл для агентов, который помогает писать обобщённые функции, рефлексию типов и код, вычисляемый на этапе компиляции в Zig. CX Codex +1 443установок zig-testing Скилл ведёт агента по тестовой системе Zig: zig build test, фильтры, comptime-проверки, тестовый аллокатор и встроенный фаззер. CX Codex +1 440установок stm32-baremetal Ведёт агента по сборке прошивки STM32 на одних заголовках CMSIS: тактовые частоты, RCC, линкерный скрипт, сборка через arm-none-eabi-gcc. CX Codex +1 360установок

Похожие

rust-async-patterns Seth Hobson Скилл помогает писать и разбирать асинхронный Rust на Tokio: задачи, каналы, потоки и обработку ошибок. Пригодится для сетевых сервисов и отладки. CX Codex 20 625установок rust-engineer https://github.com/Jeffallan Направляет агента писать и проверять идиоматичный код на Rust 2021. Помогает настроить времена жизни, связать трейты и развернуть асинхронный сервис на tokio. CX Codex +1 6 300установок rust Mindrally Задаёт агенту правила разработки на Rust: владение, async на tokio, обработка ошибок через Result и Option. CX Codex +1 778установок rust-pro Seth Hobson Субагент на модели Opus для разработки на Rust 1.75+. Помогает писать асинхронный код на Tokio и axum, настраивать многопоточность и разбираться с проверками заимствований. CX Codex 1 148установок rust-mcp-development официальный Awesome Copilot Community Плагин для GitHub Copilot, который помогает писать MCP-серверы на Rust через официальный SDK rmcp. Пригодится, если вы делаете свой сервер для ИИ-агентов. ★ 40 418звёзд репо m07-concurrency actionbook Помогает разбирать конкурентный и асинхронный Rust-код: выбрать потоки, async/await, каналы или синхронизацию. Нужен при ошибках Send/Sync, дедлоках и гонках. CX Codex +1 3 500установок

Вопросы о rust-async-internals

Как установить rust-async-internals в Claude?

Выполните npx skills add mohitmishra786/low-level-dev-skills --skill rust-async-internals -a claude-code. Скилл появится по пути ~/.claude/skills/rust-async-internals/SKILL.md.

Что делает rust-async-internals?

Ведёт агента по внутренностям async Rust: трейт Future и модель poll, Pin/Unpin, задачи tokio, диагностика через tokio-console, утечки waker и подводные камни select!/join!.

Что нужно для работы с tokio-console по этому скиллу?

Зависимость console-subscriber в Cargo.toml, фича tracing у tokio, вызов console_subscriber::init() до старта рантайма и запуск приложения с RUSTFLAGS="--cfg tokio_unstable". Сам CLI ставится командой cargo install --locked tokio-console.

Исходный текст

SKILL.md · MIT
---
name: rust-async-internals
description: Rust async internals skill for understanding and debugging async Rust. Use when understanding the Future trait and poll model, Pin and Unpin, tokio task scheduling, debugging async stack traces with tokio-console, tracking waker leaks, using select! and join!, or avoiding blocking in async contexts. Activates on queries about Rust async internals, Future poll, Pin, Unpin, tokio-console, waker, async stack traces, select!, join!, or blocking in async.
---

# Rust Async Internals

## Purpose

Guide agents through Rust async/await internals: the `Future` trait and poll loop, `Pin`/`Unpin` for self-referential types, tokio's task model, diagnosing async stack traces with tokio-console, finding waker leaks, and common `select!`/`join!` pitfalls.

## Triggers

- "How does async/await actually work in Rust?"
- "What is Pin and Unpin in async Rust?"
- "My async code is slow — how do I profile it?"
- "How do I use tokio-console to debug async tasks?"
- "I have a blocking call in async — what do I do?"
- "How does select! work and what are the pitfalls?"

## Workflow

### 1. The Future trait — poll model

```rust
// std::future::Future (simplified)
pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

pub enum Poll<T> {
    Ready(T),    // computation done, T is the result
    Pending,     // not ready yet, waker registered, will be polled again
}
```

Execution model:
1. Calling `.await` calls `poll()` on the future
2. If `Pending`: current task registers its waker and yields to the runtime
3. When the waker is triggered (I/O ready, timer fired), the runtime re-polls
4. If `Ready(val)`: the `.await` expression evaluates to `val`

### 2. Implementing a simple Future

```rust
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
    time::{Duration, Instant},
};

struct Delay { deadline: Instant }

impl Delay {
    fn new(dur: Duration) -> Self {
        Delay { deadline: Instant::now() + dur }
    }
}

impl Future for Delay {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        if Instant::now() >= self.deadline {
            Poll::Ready(())
        } else {
            // Register the waker — runtime calls waker.wake() to re-poll
            // In production: register with I/O reactor or timer wheel
            let waker = cx.waker().clone();
            let deadline = self.deadline;
            std::thread::spawn(move || {
                let now = Instant::now();
                if deadline > now {
                    std::thread::sleep(deadline - now);
                }
                waker.wake();  // notify runtime to re-poll
            });
            Poll::Pending
        }
    }
}

// Usage
async fn main() {
    Delay::new(Duration::from_secs(1)).await;
    println!("Done");
}
```

### 3. Pin and Unpin

`Pin<P>` prevents moving the value behind pointer `P`. This matters because async state machines contain self-referential pointers (a reference into the same struct where the future lives):

```rust
// Why Pin is needed: async fn compiles to a state machine struct
// that may have self-references across await points

async fn example() {
    let data = vec![1, 2, 3];
    let ref_to_data = &data;              // reference into same stack frame
    some_async_op().await;                // suspension point
    println!("{:?}", ref_to_data);       // reference still used after suspend
}
// The state machine stores both `data` and `ref_to_data`.
// If the struct were moved, `ref_to_data` would dangle.
// Pin<&mut State> prevents moving the state machine.

// Unpin: a marker trait for types that are safe to move even when pinned
// Most types implement Unpin automatically
// Futures generated by async/await do NOT implement Unpin

// Creating a Pin from Box (heap allocation → safe)
let boxed: Pin<Box<dyn Future<Output = ()>>> = Box::pin(my_future);

// Pinning to stack (unsafe, use pin! macro)
use std::pin::pin;
let fut = pin!(my_future);
fut.await;   // or poll it directly
```

### 4. tokio task model

```rust
use tokio::task;

// Spawn a task (runs concurrently on the runtime thread pool)
let handle = tokio::spawn(async {
    // ... async work ...
    42
});
let result = handle.await.unwrap();  // wait for completion

// spawn_blocking — for CPU-bound or blocking I/O
let result = task::spawn_blocking(|| {
    // runs on a dedicated blocking thread pool
    std::fs::read_to_string("big_file.txt")
}).await.unwrap();

// yield to runtime (cooperative multitasking)
tokio::task::yield_now().await;

// LocalSet — for !Send futures (single-threaded)
let local = task::LocalSet::new();
local.run_until(async {
    task::spawn_local(async { /* !Send future */ }).await.unwrap();
}).await;
```

### 5. tokio-console — async task inspector

```toml
# Cargo.toml
[dependencies]
console-subscriber = "0.3"
tokio = { version = "1", features = ["full", "tracing"] }
```

```rust
// main.rs
fn main() {
    console_subscriber::init();  // must be called before tokio runtime
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async_main());
}
```

```bash
# Install tokio-console CLI
cargo install --locked tokio-console

# Run your app with tracing enabled
RUSTFLAGS="--cfg tokio_unstable" cargo run

# In another terminal, connect tokio-console
tokio-console

# tokio-console shows:
# - Running tasks with their names, poll times, and wakeup counts
# - Slow tasks (high poll duration = blocking in async!)
# - Tasks that have been pending for a long time (stuck?)
# - Resource contention (mutex/semaphore wait times)
```

### 6. Blocking in async — common mistake

```rust
// WRONG: blocking call in async context blocks entire thread
async fn bad() {
    std::thread::sleep(Duration::from_secs(1));  // blocks runtime thread!
    std::fs::read_to_string("file.txt").unwrap(); // blocking I/O blocks runtime!
}

// CORRECT: use async equivalents
async fn good() {
    tokio::time::sleep(Duration::from_secs(1)).await;   // async sleep
    tokio::fs::read_to_string("file.txt").await.unwrap(); // async I/O
}

// CORRECT: if you must block, use spawn_blocking
async fn with_blocking() {
    let content = tokio::task::spawn_blocking(|| {
        heavy_cpu_computation()   // runs on blocking thread pool
    }).await.unwrap();
}
```

### 7. select! and join! pitfalls

```rust
use tokio::select;

// select! — complete when FIRST branch completes, cancels others
select! {
    result = fetch_a() => println!("A: {:?}", result),
    result = fetch_b() => println!("B: {:?}", result),
    // Pitfall: the LOSING branches are DROPPED immediately
    // If fetch_a wins, fetch_b's future is dropped (and its state machine cleaned up)
    // This is correct and safe — but can be surprising
}

// join! — wait for ALL to complete
let (a, b) = tokio::join!(fetch_a(), fetch_b());

// Biased select (always check first branch first)
loop {
    select! {
        biased;                        // prevents fairness, checks in order
        _ = shutdown_signal.recv() => break,
        msg = queue.recv() => process(msg),
    }
}

// select! with values from loop (use fuse)
let mut fut = some_future().fuse();   // FusedFuture: safe to poll after completion
loop {
    select! {
        val = &mut fut => { /* ... */ break; }
        _ = interval.tick() => { /* periodic work */ }
    }
}
```

## Related skills

- Use `skills/rust/rust-debugging` for GDB/LLDB debugging of async Rust programs
- Use `skills/rust/rust-profiling` for cargo-flamegraph with async stack frames
- Use `skills/low-level-programming/cpp-coroutines` for C++20 coroutine comparison
- Use `skills/low-level-programming/memory-model` for memory ordering in async contexts

© mohitmishra786 · MIT · mohitmishra786/low-level-dev-skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой