Async API Design and Task Lifecycle
Rule
Keep async signatures and spawned-task ownership explicit; reusable libraries expose awaitable work or return an owner instead of hiding tasks.
Why
Unowned tasks detach, hide failures, and make teardown and tests unreliable.
Activation
Apply this page for async APIs, async traits, Send + 'static bounds, spawning, task owners, or join handles.
Do
- Use
async fn -> Result<T, E>for one-shot work callers should await. - Introduce async traits only when callers need an abstraction.
- Add
Send + 'staticonly where values cross a spawned or stored-future boundary. - Spawn from an owner that stores handles and task-specific state.
- Use responsibility names such as
Poller,WorkerSet, orSupervisor. - Store
JoinHandle<Result<(), Error>>when failures must be reported. - Return an owner or guard when a library truly owns background work.
- Follow cancellation and shutdown for signals, joining, and teardown.
Avoid
- Do not drop a handle for important work.
- Do not assume dropping
JoinHandlecancels its task. - Do not spawn inside a constructor unless the returned value owns the lifecycle.
- Do not swallow task errors.
- Do not spawn merely to make a library API appear nonblocking.
- Do not add
Send,Sync, or'staticto ordinary async APIs by habit.
Library vs Application
Applications own spawning and task groups. Libraries normally expose futures or streams and let callers choose where to spawn.
Example
#![allow(unused)]
fn main() {
#[must_use = "call `shutdown` to stop and join the task"]
pub struct Poller {
shutdown: CancellationToken,
task: JoinHandle<Result<(), PollerError>>,
}
impl Poller {
pub async fn shutdown(self) -> Result<(), PollerError> {
self.shutdown.cancel();
self.task.await.map_err(PollerError::Join)?
}
}
}
The attribute catches a discarded return value; the explicit method owns actual cancellation and joining.
Exceptions
- Fire-and-forget only disposable, documented best-effort work.
- Tests may spawn short tasks when they abort or join them.
- Convenience APIs may spawn when they return lifecycle control.