pub trait AsyncExecutor: Send + Sync {
// Required method
fn block_on_dyn(
&self,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Box<dyn Any + Send>;
// Provided method
fn block_on_for(&self) -> Option<&'static dyn AsyncExecutor> { ... }
}Expand description
§Runtime-Agnostic Async Executor
Bridge for the #[tool] macro’s sync-from-async path that decouples
Tokitai from Tokio. The macro’s sync wrapper delegates to a registered
AsyncExecutor; install one with set_async_executor to use
async-std, smol, embassy, or any custom executor.
§Default behaviour
- Custom executor registered via
set_async_executor-> use it. - Otherwise, if inside a Tokio runtime, use the active
Handle::block_on(preserves backward compatibility). - Otherwise, return a
ToolErrorwith a descriptive English message.
§No-std fallback
When the serde feature is disabled the crate is no_std and no
executor is reachable; block_on_async always returns a
ToolError::InternalError so the macro can still type-check.
This trait is object-safe so the user-registered executor can be
stored as &'static dyn AsyncExecutor. The type-erased
AsyncExecutor::block_on_dyn takes a boxed future and returns a boxed
output; typed convenience is provided by AsyncExecutorExt.
§T-003 per-call override seam
Implementations may override AsyncExecutor::block_on_for to return
Some(...) when an ambient executor (per-call_tool, per-thread, etc.)
is reachable. The #[tool] macro’s sync-from-async wrapper probes
block_on_for first, then the global slot set via
set_async_executor, then the active Tokio runtime. This is how
async-std / smol / embassy users supply an executor without
mutating global state.
Required Methods§
Sourcefn block_on_dyn(
&self,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Box<dyn Any + Send>
fn block_on_dyn( &self, future: Pin<Box<dyn Future<Output = ()> + Send>>, ) -> Box<dyn Any + Send>
Object-safe entry point: drive a boxed future to completion on the
current thread and return its output as a boxed Any. Most users
implement AsyncExecutorExt::block_on and let the blanket impl
derive this; override it directly only for full control.
§Example
use tokitai_core::AsyncExecutor;
use core::future::Future;
use core::pin::Pin;
use core::any::Any;
struct ThreadPoolExecutor;
impl AsyncExecutor for ThreadPoolExecutor {
fn block_on_dyn(
&self,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Box<dyn Any + Send> {
// ... drive the future to completion ...
}
}Provided Methods§
Sourcefn block_on_for(&self) -> Option<&'static dyn AsyncExecutor>
fn block_on_for(&self) -> Option<&'static dyn AsyncExecutor>
Per-call override seam (T-003). The #[tool] macro’s sync
wrapper probes this before the global slot, so users who do not
want a process-wide side effect can supply an executor locally.
Return None to indicate “no ambient executor; fall through to
the global slot / Tokio probe.” The default implementation always
returns None; runtime-aware executors (async-std, smol, embassy)
override it to return Some(...) when their runtime is reachable
on the current thread.
§Example
use tokitai_core::AsyncExecutor;
struct AsyncStdExecutor;
impl AsyncExecutor for AsyncStdExecutor {
fn block_on_dyn(
&self,
future: core::pin::Pin<
Box<dyn core::future::Future<Output = ()> + Send>,
>,
) -> Box<dyn core::any::Any + Send> {
// ... drive the future ...
}
fn block_on_for(
&self,
) -> Option<&'static dyn AsyncExecutor> {
// async-std sets a thread-local handle; return `Some(self)`
// when one is reachable on this thread.
None
}
}Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".