From 5b723d58be589703f3ce01182b9bb72e11e02f0e Mon Sep 17 00:00:00 2001 From: kyren Date: Wed, 19 Jul 2017 21:28:16 -0400 Subject: [PATCH] Allow callback functions to have 'lua lifetime rather than 'static Fixes #14 --- src/lua.rs | 56 +++++++++++++++++++++++++--------------------------- src/tests.rs | 39 +++++++++++++++++++++--------------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 3c1c5c8..cdca401 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -124,10 +124,8 @@ pub trait FromLuaMulti<'a>: Sized { fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult; } -type LuaCallback = Box< - for<'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>) - -> LuaResult>, ->; +type LuaCallback<'lua> = + Box<'lua + FnMut(&'lua Lua, LuaMultiValue<'lua>) -> LuaResult>>; struct LuaRef<'lua> { lua: &'lua Lua, @@ -758,17 +756,17 @@ pub enum LuaMetaMethod { /// can be called as `userdata:method(args)` as expected. If there are any regular methods, and an /// `Index` metamethod is given, it will be called as a *fallback* if the index doesn't match an /// existing regular method. -pub struct LuaUserDataMethods { - methods: HashMap, - meta_methods: HashMap, +pub struct LuaUserDataMethods<'lua, T> { + methods: HashMap>, + meta_methods: HashMap>, _type: PhantomData, } -impl LuaUserDataMethods { +impl<'lua, T: LuaUserDataType> LuaUserDataMethods<'lua, T> { /// Add a regular method as a function which accepts a &T as the first parameter. pub fn add_method(&mut self, name: &str, method: M) - where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) - -> LuaResult> + where + M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) -> LuaResult>, { self.methods.insert( name.to_owned(), @@ -778,7 +776,7 @@ impl LuaUserDataMethods { /// Add a regular method as a function which accepts a &mut T as the first parameter. pub fn add_method_mut(&mut self, name: &str, method: M) - where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>) + where M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>) -> LuaResult> { self.methods.insert( @@ -790,8 +788,8 @@ impl LuaUserDataMethods { /// Add a regular method as a function which accepts generic arguments, the first argument will /// always be a LuaUserData of type T. pub fn add_function(&mut self, name: &str, function: F) - where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>) - -> LuaResult> + where + F: 'lua + for<'a> FnMut(&'lua Lua, LuaMultiValue<'lua>) -> LuaResult>, { self.methods.insert(name.to_owned(), Box::new(function)); } @@ -800,8 +798,8 @@ impl LuaUserDataMethods { /// error with certain binary metamethods that can trigger if ony the right side has a /// metatable. pub fn add_meta_method(&mut self, meta: LuaMetaMethod, method: M) - where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) - -> LuaResult> + where + M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) -> LuaResult>, { self.meta_methods.insert(meta, Self::box_method(method)); } @@ -810,7 +808,7 @@ impl LuaUserDataMethods { /// cause an error with certain binary metamethods that can trigger if ony the right side has a /// metatable. pub fn add_meta_method_mut(&mut self, meta: LuaMetaMethod, method: M) - where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>) + where M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>) -> LuaResult> { self.meta_methods.insert(meta, Self::box_method_mut(method)); @@ -821,15 +819,15 @@ impl LuaUserDataMethods { /// operator has a metatable, so the first argument here is not necessarily a userdata of type /// T. pub fn add_meta_function(&mut self, meta: LuaMetaMethod, function: F) - where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>) - -> LuaResult> + where + F: 'lua + for<'a> FnMut(&'lua Lua, LuaMultiValue<'lua>) -> LuaResult>, { self.meta_methods.insert(meta, Box::new(function)); } - fn box_method(mut method: M) -> LuaCallback - where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) - -> LuaResult> + fn box_method(mut method: M) -> LuaCallback<'lua> + where + M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) -> LuaResult>, { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { let userdata = LuaUserData::from_lua(front, lua)?; @@ -844,8 +842,8 @@ impl LuaUserDataMethods { } - fn box_method_mut(mut method: M) -> LuaCallback - where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>) + fn box_method_mut(mut method: M) -> LuaCallback<'lua> + where M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>) -> LuaResult> { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { @@ -869,7 +867,7 @@ pub trait LuaUserDataType: 'static + Sized { fn add_methods(_methods: &mut LuaUserDataMethods) {} } -/// Handle to an internal Lua userdata for a type that implements LuaUserDataType. Internally, +/// Handle to an internal Lua userdata for a type that implements `LuaUserDataType`. Internally, /// instances are stored in a `RefCell`, to best match the mutable semantics of the Lua language. #[derive(Clone, Debug)] pub struct LuaUserData<'lua>(LuaRef<'lua>); @@ -1120,7 +1118,7 @@ impl Lua { } /// Creates a table and fills it with values from an iterator. - pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> LuaResult + pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> LuaResult> where K: ToLua<'lua>, V: ToLua<'lua>, @@ -1142,7 +1140,7 @@ impl Lua { } /// Creates a table from an iterator of values, using `1..` as the keys. - pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> LuaResult + pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> LuaResult> where T: ToLua<'lua>, I: IntoIterator, @@ -1151,9 +1149,9 @@ impl Lua { } /// Wraps a Rust function or closure, creating a callable Lua function handle to it. - pub fn create_function(&self, func: F) -> LuaFunction + pub fn create_function<'lua, F>(&'lua self, func: F) -> LuaFunction<'lua> where - F: 'static + for<'a> FnMut(&'a Lua, LuaMultiValue<'a>) -> LuaResult>, + F: 'lua + for<'a> FnMut(&'a Lua, LuaMultiValue<'a>) -> LuaResult>, { self.create_callback_function(Box::new(func)) } @@ -1313,7 +1311,7 @@ impl Lua { T::from_lua_multi(value, self) } - fn create_callback_function(&self, func: LuaCallback) -> LuaFunction { + fn create_callback_function<'lua>(&'lua self, func: LuaCallback<'lua>) -> LuaFunction<'lua> { unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int { callback_error(state, || { let lua = Lua { diff --git a/src/tests.rs b/src/tests.rs index 7ca50e7..f28f905 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -210,25 +210,32 @@ fn test_bind() { #[test] fn test_rust_function() { - let lua = Lua::new(); - let globals = lua.globals(); - lua.exec::<()>( - r#" - function lua_function() - return rust_function() - end + let mut captured_var = 2; + { + let lua = Lua::new(); + let globals = lua.globals(); + lua.exec::<()>( + r#" + function lua_function() + return rust_function() + end - -- Test to make sure chunk return is ignored - return 1 - "#, - None, - ).unwrap(); + -- Test to make sure chunk return is ignored + return 1 + "#, + None, + ).unwrap(); - let lua_function = globals.get::<_, LuaFunction>("lua_function").unwrap(); - let rust_function = lua.create_function(|lua, _| lua.pack("hello")); + let lua_function = globals.get::<_, LuaFunction>("lua_function").unwrap(); + let rust_function = lua.create_function(|lua, _| { + captured_var = 42; + lua.pack("hello") + }); - globals.set("rust_function", rust_function).unwrap(); - assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello"); + globals.set("rust_function", rust_function).unwrap(); + assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello"); + } + assert_eq!(captured_var, 42); } #[test]