tokio-executor-trait

Implements the executor-trait Executor and Task interfaces on top of Tokio's runtime for spawning, blocking, and canceling tasks.

Library
Cargo
v3.1.0
6stars
Apache-2.0 OR MIT

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
18/100Needs Attention
Development Activity0
Maintenance0
Community12
Maturity60
Momentum0

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
51/100Fair
Architecture78
Code Quality42
Innovation60
Learning Curve25

tokio-executor-trait implements the executor-trait crate’s Executor and Task interfaces on top of Tokio’s runtime, letting libraries write against a common async-executor abstraction instead of hard-coding Tokio-specific APIs. It wraps a tokio::runtime::Handle (or the ambient current-thread handle) to provide block_on, spawn, and spawn_blocking, while forwarding task cancellation through Tokio’s JoinHandle.

It maintains dual compatibility with the crate’s previous major version via an internal tokio-executor-trait-2 dependency, and optionally instruments spawned futures with tracing spans when the tracing feature is enabled — a pattern useful for executor-agnostic libraries, such as AMQP clients, that default to Tokio but stay swappable for other runtimes.

What You Get

  • A Tokio struct implementing the shared Executor and Task traits from executor-trait, so consumers depend on one interface instead of directly wiring Tokio.
  • block_on, spawn, and spawn_blocking methods that transparently use Handle::current() when no handle was explicitly attached via with_handle.
  • Backward-compatible support for the previous major version of executor-trait through an internal shim, easing migration for downstream crates.
  • Optional tracing feature that instruments spawned futures and blocking closures with a tracing::Span for observability.

Common Use Cases

  • Executor-agnostic client libraries (e.g. AMQP/messaging clients) that need to run on Tokio by default but stay swappable for other runtimes.
  • Crates built against the executor-trait interface that want a ready-made Tokio backend instead of writing their own adapter.
  • Applications gradually migrating from an older executor-trait major version that need both old and new trait implementations to coexist.

Under The Hood

Architecture The crate is a thin one-file adapter (tokio-executor-trait/src/lib.rs) that implements the Executor/Task traits defined in the sibling executor-trait crate — the root of the same Cargo workspace — for a Tokio struct wrapping an optional tokio::runtime::Handle. There is no module hierarchy: a single struct (Tokio), a private task wrapper (TTask around tokio::task::JoinHandle), and trait impls for both the current executor-trait (v3) and a bundled tokio-executor-trait-2/executor-trait-2 pair representing the previous major version, so the same Tokio type simultaneously satisfies two API generations. Data flow is direct: block_on/spawn/spawn_blocking calls are forwarded either to an explicitly attached Handle (via with_handle) or to the ambient Handle::current(), and TTask::cancel aborts the underlying JoinHandle and awaits it. If the core Executor/Task trait definitions in the workspace’s root crate changed shape, this whole adapter would need to be rewritten, since it exists purely to satisfy that contract.

Tech Stack Rust, organized as a four-crate Cargo workspace (root executor-trait, tokio-executor-trait, smol-executor-trait, async-global-executor-trait), edition 2024, minimum Rust version 1.85. Core dependencies are async-trait (for async trait methods usable behind dyn), tokio with only the rt-multi-thread feature enabled (default features off) for Handle, JoinHandle, and spawn_blocking, and internal version/path dependencies on executor-trait (^3.0) plus a renamed executor-trait-2/tokio-executor-trait-2 pair (^2.1/^2.3) via Cargo’s package = renaming, supporting two trait-API generations at once. An optional tracing+tracing-futures pair sits behind a feature flag. CI workflows (build-and-test.yaml, lint.yaml, security.yaml, semver.yaml) build, test, lint, audit, and run cargo-semver-checks. This is a leaf runtime-adapter crate meant to be a dependency of other crates, not an application.

Code Quality No test files exist anywhere in the crate or workspace — this thin, low-surface-area adapter is validated indirectly by downstream consumers and the semver-check CI job rather than by unit tests. Error handling favors .expect() over Result propagation (for example, spawn_blocking’s failure path and TTask::poll’s completion path both panic on unexpected executor failure) rather than surfacing a typed error — a deliberate simplicity trade-off given the crate’s small scope. Naming is consistent and idiomatic Rust (Tokio, TTask, a with_handle/with_span builder pattern). No unsafe code appears in this crate, and the sibling root crate explicitly forbids it. A dedicated lint.yaml workflow runs both clippy and rustfmt --check on every push and pull request.

API Design The public surface is minimal and idiomatic: a Tokio::default()/Tokio::current() constructor pair plus builder-style with_handle/with_span methods, so getting a working executor requires close to zero boilerplate. Because it implements the shared executor-trait::Executor/Task traits, consumers write executor-agnostic code and only need this crate at the composition root, making it a well-scoped, low-friction adapter. Documentation is thin, though — there are no rustdoc comments in this crate’s own source beyond what the trait definitions inherit, and no examples directory — so downstream consumers largely learn usage from crates that already depend on it rather than from this crate’s own docs.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search