Allow callback functions to have 'lua lifetime rather than 'static

Fixes #14
This commit is contained in:
kyren 2017-07-19 21:28:16 -04:00
parent 6df96d8eb1
commit 5b723d58be
2 changed files with 50 additions and 45 deletions

View File

@ -124,10 +124,8 @@ pub trait FromLuaMulti<'a>: Sized {
fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult<Self>; fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
} }
type LuaCallback = Box< type LuaCallback<'lua> =
for<'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>) Box<'lua + FnMut(&'lua Lua, LuaMultiValue<'lua>) -> LuaResult<LuaMultiValue<'lua>>>;
-> LuaResult<LuaMultiValue<'lua>>,
>;
struct LuaRef<'lua> { struct LuaRef<'lua> {
lua: &'lua 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 /// 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 /// `Index` metamethod is given, it will be called as a *fallback* if the index doesn't match an
/// existing regular method. /// existing regular method.
pub struct LuaUserDataMethods<T> { pub struct LuaUserDataMethods<'lua, T> {
methods: HashMap<String, LuaCallback>, methods: HashMap<String, LuaCallback<'lua>>,
meta_methods: HashMap<LuaMetaMethod, LuaCallback>, meta_methods: HashMap<LuaMetaMethod, LuaCallback<'lua>>,
_type: PhantomData<T>, _type: PhantomData<T>,
} }
impl<T: LuaUserDataType> LuaUserDataMethods<T> { impl<'lua, T: LuaUserDataType> LuaUserDataMethods<'lua, T> {
/// Add a regular method as a function which accepts a &T as the first parameter. /// Add a regular method as a function which accepts a &T as the first parameter.
pub fn add_method<M>(&mut self, name: &str, method: M) pub fn add_method<M>(&mut self, name: &str, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) where
-> LuaResult<LuaMultiValue<'lua>> M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) -> LuaResult<LuaMultiValue<'lua>>,
{ {
self.methods.insert( self.methods.insert(
name.to_owned(), name.to_owned(),
@ -778,7 +776,7 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> {
/// Add a regular method as a function which accepts a &mut T as the first parameter. /// Add a regular method as a function which accepts a &mut T as the first parameter.
pub fn add_method_mut<M>(&mut self, name: &str, method: M) pub fn add_method_mut<M>(&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<LuaMultiValue<'lua>> -> LuaResult<LuaMultiValue<'lua>>
{ {
self.methods.insert( self.methods.insert(
@ -790,8 +788,8 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> {
/// Add a regular method as a function which accepts generic arguments, the first argument will /// Add a regular method as a function which accepts generic arguments, the first argument will
/// always be a LuaUserData of type T. /// always be a LuaUserData of type T.
pub fn add_function<F>(&mut self, name: &str, function: F) pub fn add_function<F>(&mut self, name: &str, function: F)
where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>) where
-> LuaResult<LuaMultiValue<'lua>> F: 'lua + for<'a> FnMut(&'lua Lua, LuaMultiValue<'lua>) -> LuaResult<LuaMultiValue<'lua>>,
{ {
self.methods.insert(name.to_owned(), Box::new(function)); self.methods.insert(name.to_owned(), Box::new(function));
} }
@ -800,8 +798,8 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> {
/// error with certain binary metamethods that can trigger if ony the right side has a /// error with certain binary metamethods that can trigger if ony the right side has a
/// metatable. /// metatable.
pub fn add_meta_method<M>(&mut self, meta: LuaMetaMethod, method: M) pub fn add_meta_method<M>(&mut self, meta: LuaMetaMethod, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) where
-> LuaResult<LuaMultiValue<'lua>> M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) -> LuaResult<LuaMultiValue<'lua>>,
{ {
self.meta_methods.insert(meta, Self::box_method(method)); self.meta_methods.insert(meta, Self::box_method(method));
} }
@ -810,7 +808,7 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> {
/// cause an error with certain binary metamethods that can trigger if ony the right side has a /// cause an error with certain binary metamethods that can trigger if ony the right side has a
/// metatable. /// metatable.
pub fn add_meta_method_mut<M>(&mut self, meta: LuaMetaMethod, method: M) pub fn add_meta_method_mut<M>(&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<LuaMultiValue<'lua>> -> LuaResult<LuaMultiValue<'lua>>
{ {
self.meta_methods.insert(meta, Self::box_method_mut(method)); self.meta_methods.insert(meta, Self::box_method_mut(method));
@ -821,15 +819,15 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> {
/// operator has a metatable, so the first argument here is not necessarily a userdata of type /// operator has a metatable, so the first argument here is not necessarily a userdata of type
/// T. /// T.
pub fn add_meta_function<F>(&mut self, meta: LuaMetaMethod, function: F) pub fn add_meta_function<F>(&mut self, meta: LuaMetaMethod, function: F)
where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>) where
-> LuaResult<LuaMultiValue<'lua>> F: 'lua + for<'a> FnMut(&'lua Lua, LuaMultiValue<'lua>) -> LuaResult<LuaMultiValue<'lua>>,
{ {
self.meta_methods.insert(meta, Box::new(function)); self.meta_methods.insert(meta, Box::new(function));
} }
fn box_method<M>(mut method: M) -> LuaCallback fn box_method<M>(mut method: M) -> LuaCallback<'lua>
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) where
-> LuaResult<LuaMultiValue<'lua>> M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>) -> LuaResult<LuaMultiValue<'lua>>,
{ {
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
let userdata = LuaUserData::from_lua(front, lua)?; let userdata = LuaUserData::from_lua(front, lua)?;
@ -844,8 +842,8 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> {
} }
fn box_method_mut<M>(mut method: M) -> LuaCallback fn box_method_mut<M>(mut method: M) -> LuaCallback<'lua>
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<LuaMultiValue<'lua>> -> LuaResult<LuaMultiValue<'lua>>
{ {
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { 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<Self>) {} fn add_methods(_methods: &mut LuaUserDataMethods<Self>) {}
} }
/// 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. /// instances are stored in a `RefCell`, to best match the mutable semantics of the Lua language.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LuaUserData<'lua>(LuaRef<'lua>); pub struct LuaUserData<'lua>(LuaRef<'lua>);
@ -1120,7 +1118,7 @@ impl Lua {
} }
/// Creates a table and fills it with values from an iterator. /// 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<LuaTable> pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> LuaResult<LuaTable<'lua>>
where where
K: ToLua<'lua>, K: ToLua<'lua>,
V: ToLua<'lua>, V: ToLua<'lua>,
@ -1142,7 +1140,7 @@ impl Lua {
} }
/// Creates a table from an iterator of values, using `1..` as the keys. /// 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<LuaTable> pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> LuaResult<LuaTable<'lua>>
where where
T: ToLua<'lua>, T: ToLua<'lua>,
I: IntoIterator<Item = T>, I: IntoIterator<Item = T>,
@ -1151,9 +1149,9 @@ impl Lua {
} }
/// Wraps a Rust function or closure, creating a callable Lua function handle to it. /// Wraps a Rust function or closure, creating a callable Lua function handle to it.
pub fn create_function<F>(&self, func: F) -> LuaFunction pub fn create_function<'lua, F>(&'lua self, func: F) -> LuaFunction<'lua>
where where
F: 'static + for<'a> FnMut(&'a Lua, LuaMultiValue<'a>) -> LuaResult<LuaMultiValue<'a>>, F: 'lua + for<'a> FnMut(&'a Lua, LuaMultiValue<'a>) -> LuaResult<LuaMultiValue<'a>>,
{ {
self.create_callback_function(Box::new(func)) self.create_callback_function(Box::new(func))
} }
@ -1313,7 +1311,7 @@ impl Lua {
T::from_lua_multi(value, self) 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 { unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || { callback_error(state, || {
let lua = Lua { let lua = Lua {

View File

@ -210,25 +210,32 @@ fn test_bind() {
#[test] #[test]
fn test_rust_function() { fn test_rust_function() {
let lua = Lua::new(); let mut captured_var = 2;
let globals = lua.globals(); {
lua.exec::<()>( let lua = Lua::new();
r#" let globals = lua.globals();
function lua_function() lua.exec::<()>(
return rust_function() r#"
end function lua_function()
return rust_function()
end
-- Test to make sure chunk return is ignored -- Test to make sure chunk return is ignored
return 1 return 1
"#, "#,
None, None,
).unwrap(); ).unwrap();
let lua_function = globals.get::<_, LuaFunction>("lua_function").unwrap(); let lua_function = globals.get::<_, LuaFunction>("lua_function").unwrap();
let rust_function = lua.create_function(|lua, _| lua.pack("hello")); let rust_function = lua.create_function(|lua, _| {
captured_var = 42;
lua.pack("hello")
});
globals.set("rust_function", rust_function).unwrap(); globals.set("rust_function", rust_function).unwrap();
assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello"); assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello");
}
assert_eq!(captured_var, 42);
} }
#[test] #[test]