| name | async-patterns-guide |
| description | Guides users on modern async patterns including native async fn in traits, async closures, and avoiding async-trait when possible. Activates when users work with async code. |
| allowed-tools | Read, Grep |
| version | 1.0.0 |
Async Patterns Guide Skill
You are an expert at modern Rust async patterns. When you detect async code, proactively suggest modern patterns and help users avoid unnecessary dependencies.
When to Activate
Activate when you notice:
- Use of async-trait crate
- Async functions in traits
- Async closures with manual construction
- Questions about async patterns or performance
Key Decision: async-trait vs Native
Use Native Async Fn (Rust 1.75+)
When:
- Static dispatch (generics)
- No dyn Trait needed
- Performance-critical code
- MSRV >= 1.75
Pattern:
trait UserRepository {
async fn find_user(&self, id: &str) -> Result<User, Error>;
async fn save_user(&self, user: &User) -> Result<(), Error>;
}
impl UserRepository for PostgresRepo {
async fn find_user(&self, id: &str) -> Result<User, Error> {
self.db.query(id).await
}
async fn save_user(&self, user: &User) -> Result<(), Error> {
self.db.insert(user).await
}
}
async fn process<R: UserRepository>(repo: R) {
let user = repo.find_user("123").await.unwrap();
}
Use async-trait Crate
When:
- Dynamic dispatch (dyn Trait) required
- Need object safety
- MSRV < 1.75
- Plugin systems or trait objects
Pattern:
use async_trait::async_trait;
#[async_trait]
trait Plugin: Send + Sync {
async fn execute(&self) -> Result<(), Error>;
}
let plugins: Vec<Box<dyn Plugin>> = vec![
Box::new(PluginA),
Box::new(PluginB),
];
for plugin in plugins {
plugin.execute().await?;
}
Migration Examples
Migrating from async-trait
Before:
use async_trait::async_trait;
#[async_trait]
trait UserService {
async fn create_user(&self, email: &str) -> Result<User, Error>;
}
#[async_trait]
impl UserService for MyService {
async fn create_user(&self, email: &str) -> Result<User, Error> {
}
}
After (if using static dispatch):
trait UserService {
async fn create_user(&self, email: &str) -> Result<User, Error>;
}
impl UserService for MyService {
async fn create_user(&self, email: &str) -> Result<User, Error> {
}
}
Async Closure Patterns
Modern Async Closures (Rust 1.85+)
async fn process_all<F>(items: Vec<Item>, f: F) -> Result<(), Error>
where
F: AsyncFn(Item) -> Result<(), Error>,
{
for item in items {
f(item).await?;
}
}
process_all(items, async |item| {
validate(&item).await?;
save(&item).await
}).await?;
Performance Considerations
Static vs Dynamic Dispatch
Static (Generics):
async fn process<R: Repository>(repo: R) {
repo.save().await;
}
Dynamic (dyn Trait):
async fn process(repo: Box<dyn Repository>) {
repo.save().await;
}
Your Approach
When you see async traits:
- Check if dyn Trait is actually needed
- Suggest removing async-trait if possible
- Explain performance benefits of native async fn
- Show migration path
Proactively help users use modern async patterns without unnecessary dependencies.