future_form_ffi 0.1.0

FFI support for future_form: host-driven polling, opaque handles, and effect slots
Documentation
//! Opaque thin-pointer handle for passing boxed futures through C ABI.

use alloc::boxed::Box;
use core::{
    future::Future,
    pin::Pin,
    task::{Context, Poll, Waker},
};

/// Opaque handle wrapping a boxed future for passing through C ABI.
///
/// A trait-object future like [`BoxFuture`](futures::future::BoxFuture)
/// is a fat pointer (data + vtable) that cannot be passed as a single
/// `*mut void` through C ABI. `HostHandle` wraps it in an outer `Box`,
/// producing a thin pointer suitable for opaque-handle FFI patterns.
///
/// # Type parameter
///
/// `F` is the future type — typically [`BoxFuture<'static, T>`] for
/// `Send` futures or [`LocalBoxFuture<'static, T>`] for `!Send` futures.
/// This keeps a single type for both cases; the `Send`-ness is carried
/// by `F` itself.
///
/// [`BoxFuture<'static, T>`]: futures::future::BoxFuture
/// [`LocalBoxFuture<'static, T>`]: futures::future::LocalBoxFuture
///
/// # Usage
///
/// Bridge authors create a handle from a boxed future, convert it to a
/// raw pointer for the C ABI, and later reconstruct it to poll or free:
///
/// ```rust,ignore
/// // In an `extern "C"` function:
/// let fut: BoxFuture<'static, u64> = Sendable::from_future(async { 42 });
/// let handle = HostHandle::new(fut);
/// let ptr = Box::into_raw(Box::new(handle)); // thin *mut for C
///
/// // Later, to poll:
/// let handle = &mut *ptr;
/// let result = handle.poll_once();
///
/// // To free:
/// drop(Box::from_raw(ptr));
/// ```
///
/// For handles that need additional per-future state (e.g., a stashed
/// effect tag), see [`EffectHandle`](crate::effect_handle::EffectHandle).
///
/// # Why not a [`LocalBoxFuture`](futures::future::LocalBoxFuture) variant?
///
/// There is no separate `LocalHostHandle` — just use
/// `HostHandle<LocalBoxFuture<'static, T>>`. The `Send` vs `!Send`
/// distinction is carried by the type parameter.
pub struct HostHandle<F>(Box<F>);

impl<F> core::fmt::Debug for HostHandle<F> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("HostHandle").finish_non_exhaustive()
    }
}

impl<F: Future + Unpin> HostHandle<F> {
    /// Wrap a boxed future in a thin-pointer handle.
    #[must_use]
    pub fn new(fut: F) -> Self {
        Self(Box::new(fut))
    }

    /// Poll the future once using a no-op waker.
    ///
    /// Returns [`Poll::Ready(T)`] when the future has resolved, or
    /// [`Poll::Pending`] if it needs to be polled again (e.g., after the
    /// host has fulfilled an effect).
    pub fn poll_once(&mut self) -> Poll<F::Output> {
        let waker = Waker::noop();
        let mut cx = Context::from_waker(waker);
        Pin::new(&mut *self.0).poll(&mut cx)
    }
}