如何实现FromLua trait将Lua中的UserData转回Rust结构体?
解决mlua中UserData类型转回Rust结构体的问题
你遇到的错误核心是:FromLua实现中误用了serde的反序列化逻辑——lua.from_value()期望输入是Lua表(table),但实际传入的是UserData类型,导致类型不匹配报错。
解决方案:直接从UserData下转为Rust结构体
mlua的UserData本质是将Rust类型实例包装后存储在Lua中,无需通过serde反序列化,直接通过UserData::downcast方法即可提取内部的Rust实例。
修改FromLua的实现如下:
impl<'lua> FromLua<'lua> for Cars { fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> { // 判断是否为UserData类型,尝试下转为Cars实例 if let Value::UserData(ud) = value { ud.borrow::<Cars>().cloned() } else { Err(mlua::Error::FromLuaConversionError { from: value.type_name(), to: "Cars", message: Some("expected userdata of type Cars".into()), }) } } }
修正后的完整代码
use serde::{Deserialize, Serialize}; use mlua::{FromLua, Lua, Value, Result, UserData, UserDataMethods}; #[derive(Default, Debug, Serialize, Deserialize, Clone )] struct Car { brand: String, model: String, } impl UserData for Car { } #[derive(Default, Debug, Serialize, Deserialize, Clone )] struct Cars { list: Vec<Car> } impl Cars { fn new() -> Self { Self { list: Vec::new() } } fn add_car(&mut self, brand: String, model: String) { self.list.push( Car { brand, model } ); } } impl UserData for Cars { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method_mut("add", |_, this, (brand, model): (String, String)| { println!("added car lua [brand: {}, name: {}]", &brand, &model); this.list.push( Car { brand, model }); println!("{:#?}", this.list); Ok(()) }); } } impl<'lua> FromLua<'lua> for Cars { fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> { if let Value::UserData(ud) = value { ud.borrow::<Cars>().cloned() } else { Err(mlua::Error::FromLuaConversionError { from: value.type_name(), to: "Cars", message: Some("expected userdata of type Cars".into()), }) } } } fn main() -> Result<()> { let lua = Lua::new(); let mut cars = Cars::new(); cars.add_car("Toyota".to_string(), "Corolla".to_string()); println!("{:#?}", &cars); lua.globals().set("Cars", cars)?; lua.load(r#" local cars = Cars cars:add("Toyota", "Lua") cars:add("Honda", "Lua") print( type(cars) ) -- userdata print( typeof(cars) ) -- Cars "#).exec()?; let final_cars: Cars = lua.globals().get("Cars")?; println!("Final car\n{:#?}", final_cars); Ok(()) }
关键说明
- 当你将实现了
UserData的Rust类型传入Lua时,mlua会自动将其包装为userdata,无需额外序列化操作 - 转回Rust时,直接通过
UserData::borrow获取内部实例的引用,再借助Clonetrait拿到所有权(如果你的类型没有实现Clone,可以考虑使用take方法转移所有权,具体需根据业务场景调整)
内容的提问来源于stack exchange,提问作者SymmetricsWeb
相关产品推荐
相关产品推荐

