pub type AsyncNativeFn = fn(*mut Vm, u32, u32) -> Pin<Box<dyn Future<Output = Result<u32, LuaError>>>>;Expand description
v1.1 B10 Stage 2 — async-native function ABI. Returns a
Pin<Box<dyn Future>> that resolves to the return-value count
(same convention as sync crate::runtime::value::NativeFn: write
results into the caller’s slot via the borrowed Vm, then yield
the count back).
§Safety contract
The first parameter is *mut Vm rather than &mut Vm because the
returned Pin<Box<dyn Future>> is 'static (the trait object
erases lifetimes) and we cannot tie it to the caller’s borrow
without for<'vm> HRTBs that the trait system rejects on dyn
futures. Implementors must reborrow inside the future:
fn my_async(
vm: *mut Vm,
func_slot: u32,
nargs: u32,
) -> Pin<Box<dyn Future<Output = Result<u32, LuaError>>>> {
Box::pin(async move {
// SAFETY: the dispatcher is suspended and EvalFuture
// holds the unique &mut Vm borrow for the future's
// entire lifetime; no concurrent access can occur.
let vm = unsafe { &mut *vm };
// ... read args from vm.stack[func_slot+1..], do async
// work (e.g. `sleep(...).await`), write results back
// to vm.stack[func_slot..], return their count ...
Ok(0)
})
}The Vm is exclusively owned by the active EvalFuture for the
suspension’s full lifetime (the dispatcher is paused; the host’s
executor is the only driver). This makes the unsafe { &mut *vm }
reborrow sound provided the future doesn’t leak the borrow past
its own await boundaries.
The native is invoked exactly once per Lua call site. The future
is polled by EvalFuture::poll; on Poll::Ready(Ok(n)) the
dispatcher resumes, treats slots [func_slot, func_slot+n) as the
return list, and continues. On Poll::Ready(Err(e)) the error
propagates as if a sync native had returned it.