pub trait LuaUserdata: 'static + Sized {
// Provided methods
fn type_name() -> &'static str { ... }
fn add_methods<M: UserdataMethods<Self>>(_m: &mut M) { ... }
fn trace(&self, _m: &mut UserdataMarker<'_>) { ... }
}Expand description
Embedder-side trait: implement on any T: 'static to expose
method-rich Lua userdata via vm.set_userdata::<T>(...).
The trait’s only required method is add_methods, which defaults
to registering nothing — yielding a userdata that still type-checks
as "userdata" but only carries identity + __name. An empty impl
(impl LuaUserdata for MyType {}) is the source-compatible bridge
for B8 callers upgrading from v1.1.
§v1.1 → v1.2 migration
v1.1 Vm::create_userdata / Vm::set_userdata accepted any
T: Any + 'static; v1.2 narrows the bound to T: LuaUserdata. Any
existing type carries over with a one-line empty impl:
struct MyType { /* … */ }
impl LuaUserdata for MyType {}§Contract on the host payload
T may hold Gc<...> fields provided it overrides trace to
mark every such handle. The default trace is a no-op, suitable
for pure host types (no Gc-managed inner state). Forgetting to
override trace when T carries a Gc<Table> / Gc<LuaStr> /
Gc<NativeClosure> / Gc<Coro> / Gc<Userdata> field whose
lifetime is not otherwise rooted risks dangling references after
collection.
v1.2 forbade Gc-bearing payloads entirely; v1.3 Phase TB lifts the
limitation by giving the trait a default trace method and
storing a monomorphic adapter in crate::runtime::userdata::UserdataPayload::Host.
Provided Methods§
Sourcefn type_name() -> &'static str
fn type_name() -> &'static str
Lua-visible type name. Used as the __name field of the
generated metatable; surfaces in tostring fallback messages and
in PUC-style "attempt to index a Counter value" errors.
Defaults to std::any::type_name.
Sourcefn add_methods<M: UserdataMethods<Self>>(_m: &mut M)
fn add_methods<M: UserdataMethods<Self>>(_m: &mut M)
Register methods + metamethods on m. Called exactly once per
T per Vm, at the first
Vm::create_userdata::<T> /
set_userdata::<T> — the resulting
metatable is cached on the Vm keyed by TypeId::of::<T>().
Sourcefn trace(&self, _m: &mut UserdataMarker<'_>)
fn trace(&self, _m: &mut UserdataMarker<'_>)
Mark every Gc-managed handle reachable from self. The default
is a no-op — override only when T directly holds
Gc<Table> / Gc<LuaStr> / Gc<NativeClosure> / Gc<Coro> /
Gc<Userdata> fields whose lifetime is not otherwise rooted
(i.e. not pinned via Vm::pin_host and not reachable from a
Lua-side table).
Called by the collector during the mark phase; the call runs
synchronously, single-threaded, and must return in bounded wall
time. The embedder must not allocate new GC objects, reenter the
Vm, take locks, or perform I/O from inside trace — see the
UserdataMarker type docs for the full contract.
§Override example
use luna_core::runtime::{Gc, Table};
use luna_core::vm::{LuaUserdata, UserdataMarker};
struct Cache { entries: Gc<Table> }
impl LuaUserdata for Cache {
fn trace(&self, m: &mut UserdataMarker) {
m.mark(self.entries);
}
}Overriding trace does not require touching any other trait
method; existing B8 / v1.2 types remain source-compatible with
an unchanged empty impl LuaUserdata for T {} (the default
no-op runs and no Gc tracing is performed).
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".