Closed alerque closed 1 month ago
To reuse a single metatable inside IntoLua
definition you can:
1) Store (once) metatable in registry using Lua::set_named_registry_value
and then retrieve when it's needed (recommended)
2) Store metatable in Lua::set_app_data
Thanks! This is an annoying thing to have to work around in LuaJIT, but that isn't mlua
's fault. I've had to accomodate this is in plain LuaJIT too.
For anybody else running into this, here in the original MWE modified as a POC for stashing a meta table in the registry as suggested:
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! mlua = { version = "0.10.0-rc.1", features = [ "luajit", "vendored" ] }
//! ```
#[derive(Debug)]
struct Thing {
value: u8,
}
fn get_metatable_from_registy(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
let key = "thing";
let metatable: mlua::Table = match lua.named_registry_value(key)? {
mlua::Value::Table(metatable) => metatable,
mlua::Value::Nil => {
let metatable = lua.create_table()?;
let less_than = lua.create_function(|_, args: (mlua::Table, mlua::Table)| {
let v1: u8 = args.0.get("value")?;
let v2: u8 = args.1.get("value")?;
Ok(v1 < v2)
})?;
metatable.set("__lt", less_than)?;
let to_string = lua.create_function(|_, a: mlua::Table| {
let v: u8 = a.get("value")?;
Ok(format!("{}", v))
})?;
metatable.set("__tostring", to_string)?;
lua.set_named_registry_value(key, &metatable)?;
metatable
}
_ => panic!("Unexpected type returned from from registry lookup"),
};
Ok(metatable)
}
impl mlua::IntoLua for Thing {
#[inline]
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
let table = lua.create_table()?;
let metatable: mlua::Table = get_metatable_from_registy(&lua)?;
table.set_metatable(Some(metatable));
table.set("value", self.value).unwrap();
Ok(mlua::Value::Table(table))
}
}
fn main() {
let test = mlua::Lua::new();
let x = Thing { value: 1 };
let y = Thing { value: 2 };
let z = Thing { value: 2 };
test.globals().set("x", x).unwrap();
test.globals().set("y", y).unwrap();
test.globals().set("z", z).unwrap();
test.load(
r#"
local mt = {
__lt = function (a, b) return a.value < b.value end,
__tostring = function (a) return a.value end,
}
local lua_thing = function (value)
return setmetatable({ value = value }, mt)
end
local a, b, c = lua_thing(1), lua_thing(2), lua_thing(2)
print(a < b, a, a.value, b, b.value, b < c, c)
print(x < y, x, x.value, y, y.value, y < z, z)
"#,
)
.exec()
.unwrap();
}
I'm trying to get a workable type system in Rust that can be used from Lua ergonomically as well. I'm having a bit of trouble with meta tables. Actually Lua 5.4 works fine, but in LuaJIT things are more cantankerous. Far example you can't set comparison functions in meta tables and use them to compare objects unless the metatable ID is actually the same across both tables. An identical meta table won't work, it has to be the same table.
Here is an MWE with some Lua chunks that work (because the
mt
is reused) and a very similar Rust version with a struct andimpl IntoLua
that does not work.How should I be setting up
IntoLua
that creates usable types on the Lua side?