How to extract user data content
Yuri6037 opened this issue · 6 comments
I have an application, where I need a scope that generates a userdata from a non Send type (application has never been intended for threading). What I'm trying to achieve is move away the content of the userdata after the scope (the user data being locked to the scope it should not need to be unsafe).
However every possible attempts I do results in compile errors, replace cannot properly infer types or refuses to accept a replacement empty instance of the type.
What I need is:
- Prepare an object with methods and rust stored data (impl it with UserData)
- Create a scope in a Lua context
- In the scope call create_static_user_data in order to make a new instance of the type
- Call a lua function with this user data as argument
- Exit the scope returning the userdata itself
- Extract the native rust data contained in this UserData
Currently the only way I can find is derive(Clone) but this has a bad side effect: cloning the content of the user data all the time is pretty slow considering this rapped type stores Vecs, Strings and HashMaps.
I just found a terrible workarround:
- First you must make a lot of code duplication for this to work. Declaring once a function is not sufficient. Declairing a impl once is only not enough...
- Unforntatly requires bad function names
Code below
- User data object
struct InstallTool
{
subprojects: Vec<String>
}
impl InstallTool
{
pub fn new() -> InstallTool
{
return InstallTool
{
subprojects: Vec::new()
};
}
}
impl UserData for InstallTool
{
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M)
{
methods.add_method_mut("SubProject", |_, this, path: String|
{
this.subprojects.push(path);
return Ok(());
});
}
}
impl UserData for &mut InstallTool //Note it's not enough to just declare UserData for InstallTool you also need &mut InstallTool
{
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) //Yeah we've got to dupe THE ENTIRE function setup, setting it up once is not enough
{
methods.add_method_mut("SubProject", |_, this, path: String|
{
this.subprojects.push(path);
return Ok(());
});
}
}
Using it
fn some_terrible_function_name(&mut self, tool: &mut InstallTool, profile: &Profile) -> rlua::Result<()>
{
return self.state.context(|ctx|
{
ctx.scope(|scope|
{
let userdata = scope.create_nonstatic_userdata(tool)?;
let mut tbl = ctx.create_table()?;
profile.fill_table(&mut tbl)?;
let func: rlua::Function = ctx.globals().get("Install")?;
func.call((tbl, userdata))?;
return Ok(());
})?;
return Ok(());
});
}
pub fn func_install(&mut self, profile: &Profile) -> Result<Vec<String>>
{
let mut tool = InstallTool::new();
if let Err(e) = self.some_terrible_function_name(&mut tool, profile)
{
return Err(Error::Lua(ErrorDomain::LuaEngine, e));
}
return Ok(tool.subprojects);
}
The RefMut normal way to do that with static user data is just completely broken: borrow_mut ALWAYS no matter what the T is result in Error mismatch userdata type.
Hi,
I'm not not sure I understand what the problem is. Have you looked at the tests for create_nonstatic_userdata
, which have examples which of how it can be used?
https://github.com/amethyst/rlua/blob/master/tests/scope.rs#L144
If you can provide a short example of what you're trying to do which isn't working, that would help us find the problem (whether it's in rlua or the example).
I was trying to do something like:
- ctx.scope
- let udata = ctx.create_static_userdata(MyType)
- extract MyType from udata
This never worked properly, so instead I used create_nonstatic_userdata with a &mut MyType instead of a MyType.
Hi,
A short, complete example would make it much easier to understand your specific problem.
But if create_nonstatic_userdata
with &mut MyType
works for you, that sounds like a perfectly good (perhaps better) way of doing things than having the object physically inside the UserData
and moving it out afterwards. Is there a problem with doing that?
Thanks,
jugglerchris
Yeah the solution works great but it requires code duplication and weird function names... Like calling the lua function with this additional rust type requires 2 rust function 1 being never called directly and another public function which allocates an object and redirects the call to the useless function. If the compiler does not optimize this we get an extra call and possible extra movs which means extra CPU cycles that could have been easily avoided by C/C++. My consern is only that in the long term this kind of code my add-up on the extra pumped CPU cycles that could be used for something else in C/C++.
EDIT: In my project, this is low scale and as such it's unlikely to play a big role. However in different types of applications where performance is critical this may be a problem
rlua is going to be deprecated soon in favour of mlua, which already support AnyUserData::take()
method to move Rust values out of Lua.
If any further functionality required please open issue in the mlua repo.