with one click
async-trait
Expert knowledge for the Rust async-trait crate — the
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Expert knowledge for the Rust async-trait crate — the
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Expert knowledge for the darkmatter Rust library - Markdown parsing, composition, frontmatter, terminal/HTML/Markdown rendering, style frontmatter, syntax highlighting, document comparison, and disclosure blocks. Use when parsing or composing Markdown, rendering Markdown to terminal/HTML/Markdown, working with DarkmatterPage, `style:` frontmatter, frontmatter hashing, disclosure blocks (`::disclosure` / `::details` / `::end-disclosure`), or comparing documents.
Use when working in the claudine/ package area or with the Claudine library/CLI — normalizing agentic-CLI lifecycle events and hooks, wrapping providers (Claude Code, Codex, Gemini, Goose, Kimi, OpenCode, Qwen, Kilo, Pi, Antigravity), composing Markdown prompts (compose/inline-compose/sequence), managing the MCP catalog, linking skills/commands/agents across providers, or researching agentic CLI platform behavior.
Expert knowledge for the unchained-ai LLM pipeline library including pipeline primitives, provider registry, model catalogs, rig-core integration, code generation, and agent status monitoring. Use when working in unchained-ai/, building LLM pipelines, adding providers/models, implementing pipeline steps, running the model generator, or querying agentic platform limits.
Expert knowledge for the Agent Client Protocol (ACP) and its underlying JSON-RPC standard, with Rust and TypeScript libraries. Use when implementing or integrating ACP, building an ACP client or agent, or devising strategies for interacting with agentic CLI providers (Claude Code, Codex, Kimi Code, OpenCode, Gemini CLI) over ACP.
Configuring models in agentic CLIs — adding local or cloud models to Claude Code, Codex, Gemini CLI, Goose, Kimi Code, OpenCode, Qwen Code, Pi, or Kilo Code. Use when wiring a local runner (Ollama, LM Studio, oMLX, llama.cpp, vLLM) into an agentic CLI, redirecting a provider's base URL (ANTHROPIC_BASE_URL, OPENAI_BASE_URL, model_providers, provider.<id>.options.baseURL), choosing between OpenAI-compatible and Anthropic-compatible endpoints, bridging a CLI to a different cloud vendor's models, or working with per-provider model config file shapes and merge semantics.
Detecting, configuring, and wiring local model runners into agentic CLIs. Use when working with Ollama, LM Studio, oMLX, llama.cpp (llama-server), or vLLM; probing which local runners are installed or running; hitting OpenAI-compatible local endpoints (/v1) or Anthropic-compatible /v1/messages; serving local models; or connecting a local model to OpenCode or Claude Code via base URL, ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN, or runner-native launch hooks.
| name | async-trait |
| description | Expert knowledge for the Rust async-trait crate — the |
| hash | async-trait-skill-v1 |
A procedural macro by David Tolnay that enables async functions in traits to work with dynamic dispatch (dyn Trait). Essential when you need trait objects with async methods.
Version: 0.1.89 (latest as of 2025) Use for: Dynamic dispatch with async traits, plugin systems, dependency injection with async interfaces.
| Scenario | Solution |
|---|---|
| Static dispatch only (generics) | Native async fn in traits (Rust 1.75+) |
Need dyn Trait / trait objects | #[async_trait] required |
| Pre-Rust 1.75 compatibility | #[async_trait] required |
| Performance-critical tight loops | Consider avoiding trait objects entirely |
Key insight: Native async traits (Rust 1.75+) do NOT support dyn Trait. If you need trait objects with async methods, async-trait is still required.
Apply #[async_trait] to both trait definition AND all implementations:
use async_trait::async_trait;
#[async_trait]
pub trait ModelScanner: Send + Sync {
async fn scan(&self) -> Result<Vec<Model>, ScanError>;
async fn is_available(&self) -> bool;
fn name(&self) -> &'static str; // Non-async methods work normally
}
#[async_trait]
impl ModelScanner for OllamaScanner {
async fn scan(&self) -> Result<Vec<Model>, ScanError> {
// Implementation
}
async fn is_available(&self) -> bool {
self.client.health_check().await.is_ok()
}
fn name(&self) -> &'static str {
"ollama"
}
}
The macro transforms async methods into boxed futures:
// Your code:
async fn scan(&self) -> Vec<Model>;
// Expands to:
fn scan<'async_trait>(&'async_trait self)
-> Pin<Box<dyn Future<Output = Vec<Model>> + Send + 'async_trait>>
where
Self: Sync + 'async_trait
{
Box::pin(async move { /* your implementation */ })
}
Default behavior: Futures are Send (can move between threads).
// Default: Send bound on future
#[async_trait]
trait MyTrait {
async fn method(&self); // Future: Pin<Box<dyn Future + Send>>
}
For single-threaded contexts (e.g., !Send types, Rc, RefCell):
// Remove Send bound - use on BOTH trait and impl
#[async_trait(?Send)]
trait LocalTrait {
async fn method(&self); // Future: Pin<Box<dyn Future>> (no Send)
}
#[async_trait(?Send)]
impl LocalTrait for MyType {
async fn method(&self) { /* ... */ }
}
use async_trait::async_trait;
#[async_trait]
pub trait Scanner: Send + Sync {
async fn scan(&self) -> Vec<Item>;
}
// Registry holding boxed trait objects
pub struct Registry {
scanners: Vec<Box<dyn Scanner>>,
}
impl Registry {
pub fn add(&mut self, scanner: impl Scanner + 'static) {
self.scanners.push(Box::new(scanner));
}
pub async fn scan_all(&self) -> Vec<Item> {
let futures: Vec<_> = self.scanners.iter().map(|s| s.scan()).collect();
futures::future::join_all(futures).await.into_iter().flatten().collect()
}
}
Overhead per call: ~20 nanoseconds (heap allocation for boxed future)
When it matters:
When it doesn't matter (most cases):
Benchmark perspective: 100K calls = ~2ms overhead. Usually negligible compared to actual I/O.
#[async_trait]
trait MyTrait { async fn method(&self); }
// WRONG: Missing #[async_trait]
impl MyTrait for MyType {
async fn method(&self) { } // Compile error!
}
// CORRECT:
#[async_trait]
impl MyTrait for MyType {
async fn method(&self) { }
}
#[async_trait] // Send bound
trait MyTrait { ... }
#[async_trait(?Send)] // No Send bound - MISMATCH!
impl MyTrait for MyType { ... } // Compile error
// Won't work as dyn Trait:
#[async_trait]
trait BadTrait {
async fn method(&self);
}
// Works as dyn Trait:
#[async_trait]
trait GoodTrait: Send + Sync {
async fn method(&self);
}
let scanner: Box<dyn GoodTrait> = Box::new(MyImpl); // Works!
Async-trait supports lifetime elision in & and &mut references only:
#[async_trait]
trait Valid {
async fn process(&self, data: &str); // OK: elision works
}
#[async_trait]
trait NeedsExplicit {
// Must use explicit lifetime or '_ for non-reference types
async fn process(&self, data: Cow<'_, str>);
}
For native async traits that need Send bounds without full boxing:
use trait_variant::make;
#[trait_variant::make(SendScanner: Send)]
trait LocalScanner {
async fn scan(&self) -> Vec<Model>;
}
// Generates two traits:
// - LocalScanner: no Send bound
// - SendScanner: with Send bound on futures
Limitation: Still no dyn Trait support - use async-trait for that.