From 4549abbb850808a9799602f02f7548ea2080aee7 Mon Sep 17 00:00:00 2001 From: kyren Date: Mon, 31 Jul 2017 01:21:41 -0400 Subject: [PATCH 1/5] Two major API changes to start with: * Callbacks have generic argument and return types * All variadics are done with tuples --- Cargo.toml | 3 - examples/examples.rs | 14 ++--- src/lib.rs | 3 - src/lua.rs | 107 +++++++++++++++++++++++------------- src/multi.rs | 127 ++++++++++++++++++++++++------------------- src/tests.rs | 90 +++++++++++++++--------------- 6 files changed, 190 insertions(+), 154 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad713a0..c423229 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,3 @@ builtin-lua = ["gcc"] [build-dependencies] gcc = { version = "0.3", optional = true } - -[dependencies] -hlist-macro = "0.1" diff --git a/examples/examples.rs b/examples/examples.rs index 15e034d..51c363c 100644 --- a/examples/examples.rs +++ b/examples/examples.rs @@ -1,5 +1,3 @@ -#[macro_use] -extern crate hlist_macro; extern crate rlua; use std::f32; @@ -82,9 +80,7 @@ fn examples() -> Result<()> { // This API handles variadics using Heterogeneous Lists. This is one way to // call a function with multiple parameters: - print.call::<_, ()>( - hlist!["hello", "again", "from", "rust"], - )?; + print.call::<_, ()>(("hello", "again", "from", "rust"))?; // You can bind rust functions to lua as well @@ -95,7 +91,7 @@ fn examples() -> Result<()> { // signature limitations, this cannot be done automatically from the // function signature, but this will be fixed with ATCs. Notice the use // of the hlist macros again. - let hlist_pat![list1, list2] = lua.unpack::, Vec]>(args)?; + let (list1, list2) = lua.unpack::<(Vec, Vec)>(args)?; // This function just checks whether two string lists are equal, and in // an inefficient way. Results are returned with lua.pack, which takes @@ -141,20 +137,20 @@ fn examples() -> Result<()> { impl UserData for Vec2 { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("magnitude", |lua, vec, _| { + methods.add_method("magnitude", |lua, vec, _: ()| { let mag_squared = vec.0 * vec.0 + vec.1 * vec.1; lua.pack(mag_squared.sqrt()) }); methods.add_meta_function(MetaMethod::Add, |lua, params| { - let hlist_pat![vec1, vec2] = lua.unpack::(params)?; + let (vec1, vec2) = lua.unpack::<(Vec2, Vec2)>(params)?; lua.pack(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1)) }); } } let vec2_constructor = lua.create_function(|lua, args| { - let hlist_pat![x, y] = lua.unpack::(args)?; + let (x, y) = lua.unpack::<(f32, f32)>(args)?; lua.pack(Vec2(x, y)) }); globals.set("vec2", vec2_constructor)?; diff --git a/src/lib.rs b/src/lib.rs index f8c3543..ea2a078 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,6 @@ // Deny warnings inside doc tests / examples #![doc(test(attr(deny(warnings))))] -#[cfg_attr(test, macro_use)] -extern crate hlist_macro; - pub mod ffi; #[macro_use] mod util; diff --git a/src/lua.rs b/src/lua.rs index 692d16d..5ffeadd 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -647,7 +647,6 @@ impl<'lua> Function<'lua> { /// /// ``` /// # extern crate rlua; - /// # #[macro_use] extern crate hlist_macro; /// # use rlua::{Lua, Function, Result}; /// # fn try_main() -> Result<()> { /// let lua = Lua::new(); @@ -658,7 +657,7 @@ impl<'lua> Function<'lua> { /// end /// "#, None)?; /// - /// assert_eq!(sum.call::<_, u32>(hlist![3, 4])?, 3 + 4); + /// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4); /// /// # Ok(()) /// # } @@ -966,9 +965,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// /// If `add_meta_method` is used to override the `__index` metamethod, this approach will fall /// back to the user-provided metamethod if no regular method was found. - pub fn add_method(&mut self, name: &str, method: M) + pub fn add_method(&mut self, name: &str, method: M) where - M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result, { self.methods.insert( name.to_owned(), @@ -981,9 +982,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// Refer to [`add_method`] for more information about the implementation. /// /// [`add_method`]: #method.add_method - pub fn add_method_mut(&mut self, name: &str, method: M) + pub fn add_method_mut(&mut self, name: &str, method: M) where - M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result, { self.methods.insert( name.to_owned(), @@ -998,11 +1001,16 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// /// [`add_method`]: #method.add_method /// [`add_method_mut`]: #method.add_method_mut - pub fn add_function(&mut self, name: &str, function: F) + pub fn add_function(&mut self, name: &str, function: F) where - F: 'lua + FnMut(&'lua Lua, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + F: 'lua + FnMut(&'lua Lua, A) -> Result, { - self.methods.insert(name.to_owned(), Box::new(function)); + self.methods.insert( + name.to_owned(), + Self::box_function(function), + ); } /// Add a metamethod which accepts a `&T` as the first parameter. @@ -1013,9 +1021,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// side has a metatable. To prevent this, use [`add_meta_function`]. /// /// [`add_meta_function`]: #method.add_meta_function - pub fn add_meta_method(&mut self, meta: MetaMethod, method: M) + pub fn add_meta_method(&mut self, meta: MetaMethod, method: M) where - M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result, { self.meta_methods.insert(meta, Self::box_method(method)); } @@ -1028,9 +1038,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// side has a metatable. To prevent this, use [`add_meta_function`]. /// /// [`add_meta_function`]: #method.add_meta_function - pub fn add_meta_method_mut(&mut self, meta: MetaMethod, method: M) + pub fn add_meta_method_mut(&mut self, meta: MetaMethod, method: M) where - M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result, { self.meta_methods.insert(meta, Self::box_method_mut(method)); } @@ -1040,21 +1052,39 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// Metamethods for binary operators can be triggered if either the left or right argument to /// the binary 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: MetaMethod, function: F) + pub fn add_meta_function(&mut self, meta: MetaMethod, function: F) where - F: 'lua + FnMut(&'lua Lua, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + F: 'lua + FnMut(&'lua Lua, A) -> Result, { - self.meta_methods.insert(meta, Box::new(function)); + self.meta_methods.insert(meta, Self::box_function(function)); } - fn box_method(mut method: M) -> Callback<'lua> + fn box_function(mut function: F) -> Callback<'lua> where - M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + F: 'lua + for<'a> FnMut(&'lua Lua, A) -> Result, + { + Box::new(move |lua, args| { + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi( + lua, + ) + }) + } + + fn box_method(mut method: M) -> Callback<'lua> + where + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + M: 'lua + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result, { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { let userdata = AnyUserData::from_lua(front, lua)?; let userdata = userdata.borrow::()?; - method(lua, &userdata, args) + method(lua, &userdata, A::from_lua_multi(args, lua)?)? + .to_lua_multi(lua) } else { Err(Error::FromLuaConversionError( "No userdata supplied as first argument to method" @@ -1063,14 +1093,17 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { }) } - fn box_method_mut(mut method: M) -> Callback<'lua> + fn box_method_mut(mut method: M) -> Callback<'lua> where - M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + M: 'lua + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result, { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { let userdata = AnyUserData::from_lua(front, lua)?; let mut userdata = userdata.borrow_mut::()?; - method(lua, &mut userdata, args) + method(lua, &mut userdata, A::from_lua_multi(args, lua)?)? + .to_lua_multi(lua) } else { Err( Error::FromLuaConversionError( @@ -1078,7 +1111,6 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { ).into(), ) }) - } } @@ -1121,21 +1153,17 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> { /// /// impl UserData for MyUserData { /// fn add_methods(methods: &mut UserDataMethods) { -/// methods.add_method("get", |lua, this, args| { -/// # let _ = (lua, args); // used -/// lua.pack(this.0) +/// methods.add_method("get", |_, this, _: ()| { +/// Ok(this.0) /// }); /// -/// methods.add_method_mut("add", |lua, this, args| { -/// let value: i32 = lua.unpack(args)?; -/// +/// methods.add_method_mut("add", |_, this, value: i32| { /// this.0 += value; -/// lua.pack(()) +/// Ok(()) /// }); /// -/// methods.add_meta_method(MetaMethod::Add, |lua, this, args| { -/// let value: i32 = lua.unpack(args)?; -/// lua.pack(this.0 + value) +/// methods.add_meta_method(MetaMethod::Add, |_, this, value: i32| { +/// Ok(this.0 + value) /// }); /// } /// } @@ -1494,14 +1522,13 @@ impl Lua { /// Use the `hlist_macro` crate to use multiple arguments: /// /// ``` - /// #[macro_use] extern crate hlist_macro; /// # extern crate rlua; /// # use rlua::{Lua, Result}; /// # fn try_main() -> Result<()> { /// let lua = Lua::new(); /// /// let print_person = lua.create_function(|lua, args| { - /// let hlist_pat![name, age]: HList![String, u8] = lua.unpack(args)?; + /// let (name, age): (String, u8) = lua.unpack(args)?; /// println!("{} is {} years old!", name, age); /// lua.pack(()) /// }); @@ -1512,11 +1539,15 @@ impl Lua { /// # try_main().unwrap(); /// # } /// ``` - pub fn create_function<'lua, F>(&'lua self, func: F) -> Function<'lua> + pub fn create_function<'lua, A, R, F>(&'lua self, mut func: F) -> Function<'lua> where - F: 'lua + FnMut(&'lua Lua, MultiValue<'lua>) -> Result>, + A: 'lua + FromLuaMulti<'lua>, + R: 'lua + ToLuaMulti<'lua>, + F: 'lua + FnMut(&'lua Lua, A) -> Result, { - self.create_callback_function(Box::new(func)) + self.create_callback_function(Box::new(move |lua, args| { + func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })) } /// Wraps a Lua function into a new thread (or coroutine). diff --git a/src/multi.rs b/src/multi.rs index 65051ce..1aed22d 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -1,22 +1,8 @@ use std::result::Result as StdResult; -use hlist_macro::{HNil, HCons}; - use error::*; use lua::*; -impl<'lua> ToLuaMulti<'lua> for () { - fn to_lua_multi(self, _: &'lua Lua) -> Result { - Ok(MultiValue::new()) - } -} - -impl<'lua> FromLuaMulti<'lua> for () { - fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result { - Ok(()) - } -} - /// Result is convertible to `MultiValue` following the common lua idiom of returning the result /// on success, or in the case of an error, returning nil followed by the error impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult { @@ -83,48 +69,79 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic { } } -impl<'lua> ToLuaMulti<'lua> for HNil { - fn to_lua_multi(self, _: &'lua Lua) -> Result> { - Ok(MultiValue::new()) - } +macro_rules! impl_tuple { + () => ( + impl<'lua> ToLuaMulti<'lua> for () { + fn to_lua_multi(self, _: &'lua Lua) -> Result { + Ok(MultiValue::new()) + } + } + + impl<'lua> FromLuaMulti<'lua> for () { + fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result { + Ok(()) + } + } + ); + + ($last:ident $($name:ident)*) => ( + impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,) + where $($name: ToLua<'lua>,)* + $last: ToLuaMulti<'lua> + { + #[allow(unused_mut)] + #[allow(non_snake_case)] + fn to_lua_multi(self, lua: &'lua Lua) -> Result> { + let ($($name,)* $last,) = self; + + let mut results = $last.to_lua_multi(lua)?; + push_reverse!(results, $($name.to_lua(lua)?,)*); + Ok(results) + } + } + + impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,) + where $($name: FromLua<'lua>,)* + $last: FromLuaMulti<'lua> + { + #[allow(unused_mut)] + #[allow(non_snake_case)] + fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result { + $(let $name = values.pop_front().unwrap_or(Nil);)* + let $last = FromLuaMulti::from_lua_multi(values, lua)?; + Ok(($(FromLua::from_lua($name, lua)?,)* $last,)) + } + } + ); } -impl<'lua> FromLuaMulti<'lua> for HNil { - fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result { - Ok(HNil) - } +macro_rules! push_reverse { + ($multi_value:expr, $first:expr, $($rest:expr,)*) => ( + push_reverse!($multi_value, $($rest,)*); + $multi_value.push_front($first); + ); + + ($multi_value:expr, $first:expr) => ( + $multi_value.push_front($first); + ); + + ($multi_value:expr,) => (); } -impl<'lua, T: ToLuaMulti<'lua>> ToLuaMulti<'lua> for HCons { - fn to_lua_multi(self, lua: &'lua Lua) -> Result> { - self.0.to_lua_multi(lua) - } -} - -impl<'lua, T: FromLuaMulti<'lua>> FromLuaMulti<'lua> for HCons { - fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result { - Ok(HCons(T::from_lua_multi(values, lua)?, HNil)) - } -} - -impl<'lua, H: ToLua<'lua>, A, B> ToLuaMulti<'lua> for HCons> -where - HCons: ToLuaMulti<'lua>, -{ - fn to_lua_multi(self, lua: &'lua Lua) -> Result> { - let mut results = self.1.to_lua_multi(lua)?; - results.push_front(self.0.to_lua(lua)?); - Ok(results) - } -} - -impl<'lua, H: FromLua<'lua>, A, B> FromLuaMulti<'lua> for HCons> -where - HCons: FromLuaMulti<'lua>, -{ - fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result { - let val = H::from_lua(values.pop_front().unwrap_or(Nil), lua)?; - let res = HCons::::from_lua_multi(values, lua)?; - Ok(HCons(val, res)) - } -} +impl_tuple! { } +impl_tuple! { A } +impl_tuple! { A B } +impl_tuple! { A B C } +impl_tuple! { A B C D } +impl_tuple! { A B C D E } +impl_tuple! { A B C D E F } +impl_tuple! { A B C D E F G } +impl_tuple! { A B C D E F G H } +impl_tuple! { A B C D E F G H I } +impl_tuple! { A B C D E F G H I J } +impl_tuple! { A B C D E F G H I J K } +impl_tuple! { A B C D E F G H I J K L } +impl_tuple! { A B C D E F G H I J K L M } +impl_tuple! { A B C D E F G H I J K L M N } +impl_tuple! { A B C D E F G H I J K L M N O } +impl_tuple! { A B C D E F G H I J K L M N O P } diff --git a/src/tests.rs b/src/tests.rs index bf64b6b..f5d0ad1 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -173,10 +173,7 @@ fn test_function() { ).unwrap(); let concat = globals.get::<_, Function>("concat").unwrap(); - assert_eq!( - concat.call::<_, String>(hlist!["foo", "bar"]).unwrap(), - "foobar" - ); + assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar"); } #[test] @@ -199,9 +196,9 @@ fn test_bind() { let mut concat = globals.get::<_, Function>("concat").unwrap(); concat = concat.bind("foo").unwrap(); concat = concat.bind("bar").unwrap(); - concat = concat.bind(hlist!["baz", "baf"]).unwrap(); + concat = concat.bind(("baz", "baf")).unwrap(); assert_eq!( - concat.call::<_, String>(hlist!["hi", "wut"]).unwrap(), + concat.call::<_, String>(("hi", "wut")).unwrap(), "foobarbazbafhiwut" ); } @@ -225,9 +222,9 @@ fn test_rust_function() { ).unwrap(); let lua_function = globals.get::<_, Function>("lua_function").unwrap(); - let rust_function = lua.create_function(|lua, _| { + let rust_function = lua.create_function(|_, _: ()| { captured_var = 42; - lua.pack("hello") + Ok("hello") }); globals.set("rust_function", rust_function).unwrap(); @@ -264,10 +261,10 @@ fn test_methods() { impl UserData for MyUserData { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("get_value", |lua, data, _| lua.pack(data.0)); - methods.add_method_mut("set_value", |lua, data, args| { - data.0 = lua.unpack(args)?; - lua.pack(()) + methods.add_method("get_value", |_, data, _: ()| Ok(data.0)); + methods.add_method_mut("set_value", |_, data, args| { + data.0 = args; + Ok(()) }); } } @@ -304,19 +301,16 @@ fn test_metamethods() { impl UserData for MyUserData { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("get", |lua, data, _| lua.pack(data.0)); - methods.add_meta_function(MetaMethod::Add, |lua, args| { - let hlist_pat![lhs, rhs] = lua.unpack::(args)?; - lua.pack(MyUserData(lhs.0 + rhs.0)) + methods.add_method("get", |_, data, _: ()| Ok(data.0)); + methods.add_meta_function(MetaMethod::Add, |_, (lhs, rhs): (MyUserData, MyUserData)| { + Ok(MyUserData(lhs.0 + rhs.0)) }); - methods.add_meta_function(MetaMethod::Sub, |lua, args| { - let hlist_pat![lhs, rhs] = lua.unpack::(args)?; - lua.pack(MyUserData(lhs.0 - rhs.0)) + methods.add_meta_function(MetaMethod::Sub, |_, (lhs, rhs): (MyUserData, MyUserData)| { + Ok(MyUserData(lhs.0 - rhs.0)) }); - methods.add_meta_method(MetaMethod::Index, |lua, data, args| { - let index = lua.unpack::(args)?; + methods.add_meta_method(MetaMethod::Index, |_, data, index: LuaString| { if index.to_str()? == "inner" { - lua.pack(data.0) + Ok(data.0) } else { Err("no such custom index".to_lua_err()) } @@ -401,14 +395,10 @@ fn test_lua_multi() { let concat = globals.get::<_, Function>("concat").unwrap(); let mreturn = globals.get::<_, Function>("mreturn").unwrap(); - assert_eq!( - concat.call::<_, String>(hlist!["foo", "bar"]).unwrap(), - "foobar" - ); - let hlist_pat![a, b] = mreturn.call::<_, HList![u64, u64]>(hlist![]).unwrap(); + assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar"); + let (a, b) = mreturn.call::<_, (u64, u64)>(()).unwrap(); assert_eq!((a, b), (1, 2)); - let hlist_pat![a, b, Variadic(v)] = - mreturn.call::<_, HList![u64, u64, Variadic]>(hlist![]).unwrap(); + let (a, b, Variadic(v)) = mreturn.call::<_, (u64, u64, Variadic)>(()).unwrap(); assert_eq!((a, b), (1, 2)); assert_eq!(v, vec![3, 4, 5, 6]); } @@ -507,7 +497,8 @@ fn test_error() { None, ).unwrap(); - let rust_error_function = lua.create_function(|_, _| Err(TestError.to_lua_err())); + let rust_error_function = + lua.create_function(|_, _: ()| -> Result<()> { Err(TestError.to_lua_err()) }); globals .set("rust_error_function", rust_error_function) .unwrap(); @@ -566,7 +557,7 @@ fn test_error() { "#, None, )?; - let rust_panic_function = lua.create_function(|_, _| { + let rust_panic_function = lua.create_function(|_, _: ()| -> Result<()> { panic!("expected panic, this panic should be caught in rust") }); globals.set("rust_panic_function", rust_panic_function)?; @@ -592,7 +583,7 @@ fn test_error() { "#, None, )?; - let rust_panic_function = lua.create_function(|_, _| { + let rust_panic_function = lua.create_function(|_, _: ()| -> Result<()> { panic!("expected panic, this panic should be caught in rust") }); globals.set("rust_panic_function", rust_panic_function)?; @@ -749,12 +740,12 @@ fn test_result_conversions() { let lua = Lua::new(); let globals = lua.globals(); - let err = lua.create_function(|lua, _| { - lua.pack(Err::( + let err = lua.create_function(|_, _: ()| { + Ok(Err::( "only through failure can we succeed".to_lua_err(), )) }); - let ok = lua.create_function(|lua, _| lua.pack(Ok::<_, Error>("!".to_owned()))); + let ok = lua.create_function(|_, _: ()| Ok(Ok::<_, Error>("!".to_owned()))); globals.set("err", err).unwrap(); globals.set("ok", ok).unwrap(); @@ -806,9 +797,9 @@ fn test_expired_userdata() { impl UserData for MyUserdata { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("access", |lua, this, _| { + methods.add_method("access", |_, this, _: ()| { assert!(this.id == 123); - lua.pack(()) + Ok(()) }); } } @@ -867,17 +858,26 @@ fn detroys_userdata() { #[test] fn string_views() { let lua = Lua::new(); - lua.eval::<()>(r#" + lua.eval::<()>( + r#" ok = "null bytes are valid utf-8, wh\0 knew?" err = "but \xff isn't :(" - "#, None).unwrap(); + "#, + None, + ).unwrap(); let globals = lua.globals(); let ok: LuaString = globals.get("ok").unwrap(); let err: LuaString = globals.get("err").unwrap(); - assert_eq!(ok.to_str().unwrap(), "null bytes are valid utf-8, wh\0 knew?"); - assert_eq!(ok.as_bytes(), &b"null bytes are valid utf-8, wh\0 knew?"[..]); + assert_eq!( + ok.to_str().unwrap(), + "null bytes are valid utf-8, wh\0 knew?" + ); + assert_eq!( + ok.as_bytes(), + &b"null bytes are valid utf-8, wh\0 knew?"[..] + ); assert!(err.to_str().is_err()); assert_eq!(err.as_bytes(), &b"but \xff isn't :("[..]); @@ -886,9 +886,7 @@ fn string_views() { #[test] fn coroutine_from_closure() { let lua = Lua::new(); - let thrd_main = lua.create_function(|lua, _| { - lua.pack(()) - }); + let thrd_main = lua.create_function(|_, _: ()| Ok(())); lua.globals().set("main", thrd_main).unwrap(); let thrd: Thread = lua.eval("coroutine.create(main)", None).unwrap(); thrd.resume::<_, ()>(()).unwrap(); @@ -898,10 +896,10 @@ fn coroutine_from_closure() { #[should_panic] fn coroutine_panic() { let lua = Lua::new(); - let thrd_main = lua.create_function(|lua, _| { + let thrd_main = lua.create_function(|lua, _: ()| { // whoops, 'main' has a wrong type let _coro: u32 = lua.globals().get("main").unwrap(); - lua.pack(()) + Ok(()) }); lua.globals().set("main", thrd_main.clone()).unwrap(); let thrd: Thread = lua.create_thread(thrd_main); From d02e32a80a88a94cacb4ef9952474a8fa0d28d1a Mon Sep 17 00:00:00 2001 From: kyren Date: Mon, 31 Jul 2017 01:27:35 -0400 Subject: [PATCH 2/5] Remove the rest of the uses of pack / unpack --- examples/examples.rs | 31 ++++++++++--------------------- src/lua.rs | 10 ++++------ 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/examples/examples.rs b/examples/examples.rs index 51c363c..f8afdd3 100644 --- a/examples/examples.rs +++ b/examples/examples.rs @@ -84,31 +84,22 @@ fn examples() -> Result<()> { // You can bind rust functions to lua as well - let check_equal = lua.create_function(|lua, args| { - // Functions wrapped in lua receive their arguments packed together as - // MultiValue. The first thing that most wrapped functions will do - // is "unpack" this MultiValue into its parts. Due to lifetime type - // signature limitations, this cannot be done automatically from the - // function signature, but this will be fixed with ATCs. Notice the use - // of the hlist macros again. - let (list1, list2) = lua.unpack::<(Vec, Vec)>(args)?; - + let check_equal = lua.create_function(|_, (list1, list2): (Vec, Vec)| { // This function just checks whether two string lists are equal, and in // an inefficient way. Results are returned with lua.pack, which takes // any number of values and turns them back into MultiValue. In this // way, multiple values can also be returned to Lua. Again, this cannot // be inferred as part of the function signature due to the same // lifetime type signature limitations. - lua.pack(list1 == list2) + Ok(list1 == list2) }); globals.set("check_equal", check_equal)?; // You can also accept variadic arguments to rust callbacks. - let join = lua.create_function(|lua, args| { - let strings = lua.unpack::>(args)?.0; + let join = lua.create_function(|_, strings: Variadic| { // (This is quadratic!, it's just an example!) - lua.pack(strings.iter().fold("".to_owned(), |a, b| a + b)) + Ok(strings.0.iter().fold("".to_owned(), |a, b| a + b)) }); globals.set("join", join)?; @@ -137,21 +128,19 @@ fn examples() -> Result<()> { impl UserData for Vec2 { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("magnitude", |lua, vec, _: ()| { + methods.add_method("magnitude", |_, vec, _: ()| { let mag_squared = vec.0 * vec.0 + vec.1 * vec.1; - lua.pack(mag_squared.sqrt()) + Ok(mag_squared.sqrt()) }); - methods.add_meta_function(MetaMethod::Add, |lua, params| { - let (vec1, vec2) = lua.unpack::<(Vec2, Vec2)>(params)?; - lua.pack(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1)) + methods.add_meta_function(MetaMethod::Add, |_, (vec1, vec2): (Vec2, Vec2)| { + Ok(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1)) }); } } - let vec2_constructor = lua.create_function(|lua, args| { - let (x, y) = lua.unpack::<(f32, f32)>(args)?; - lua.pack(Vec2(x, y)) + let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| { + Ok(Vec2(x, y)) }); globals.set("vec2", vec2_constructor)?; diff --git a/src/lua.rs b/src/lua.rs index 5ffeadd..fcd1a87 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -1506,10 +1506,9 @@ impl Lua { /// # fn try_main() -> Result<()> { /// let lua = Lua::new(); /// - /// let greet = lua.create_function(|lua, args| { - /// let name: String = lua.unpack(args)?; + /// let greet = lua.create_function(|_, name: String| { /// println!("Hello, {}!", name); - /// lua.pack(()) + /// Ok(()) /// }); /// # let _ = greet; // used /// # Ok(()) @@ -1527,10 +1526,9 @@ impl Lua { /// # fn try_main() -> Result<()> { /// let lua = Lua::new(); /// - /// let print_person = lua.create_function(|lua, args| { - /// let (name, age): (String, u8) = lua.unpack(args)?; + /// let print_person = lua.create_function(|_, (name, age): (String, u8)| { /// println!("{} is {} years old!", name, age); - /// lua.pack(()) + /// Ok(()) /// }); /// # let _ = print_person; // used /// # Ok(()) From 48bf35dc5b872cd446ef7ba377246bf50950b9e3 Mon Sep 17 00:00:00 2001 From: kyren Date: Tue, 1 Aug 2017 13:55:08 -0400 Subject: [PATCH 3/5] Fix references to hlists in documentation, improve? Variadic usability Also rename to/from/pack/unpack to pack/unpack/pack_multi/unpack_multi, I don't know if this makes their usage clearer, and it IS a bit confusing that I'm changing the meaning of the words 'pack' and 'unpack' --- examples/examples.rs | 27 ++++++++++++--------------- src/lua.rs | 31 +++++++++++++++---------------- src/multi.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- src/tests.rs | 17 ++++++++--------- 4 files changed, 76 insertions(+), 43 deletions(-) diff --git a/examples/examples.rs b/examples/examples.rs index f8afdd3..e045b98 100644 --- a/examples/examples.rs +++ b/examples/examples.rs @@ -77,29 +77,28 @@ fn examples() -> Result<()> { let print: Function = globals.get("print")?; print.call::<_, ()>("hello from rust")?; - // This API handles variadics using Heterogeneous Lists. This is one way to - // call a function with multiple parameters: + // This API handles variadics using tuples. This is one way to call a function with multiple + // parameters: print.call::<_, ()>(("hello", "again", "from", "rust"))?; - // You can bind rust functions to lua as well - + // You can bind rust functions to lua as well. Callbacks receive the lua state itself as their + // first parameter, and the arguments given to the function as the second parameter. The type + // of the arguments can be anything that is convertible from the set of parameters, in this + // case, the function expects two string sequences. let check_equal = lua.create_function(|_, (list1, list2): (Vec, Vec)| { - // This function just checks whether two string lists are equal, and in - // an inefficient way. Results are returned with lua.pack, which takes - // any number of values and turns them back into MultiValue. In this - // way, multiple values can also be returned to Lua. Again, this cannot - // be inferred as part of the function signature due to the same - // lifetime type signature limitations. + // This function just checks whether two string lists are equal, and in an inefficient way. + // Lua callbacks return rlua::Result, an Ok value is a normal return, and an Err return + // turns into a Lua 'error'. Again, any type that is convertible to lua may be returned. Ok(list1 == list2) }); globals.set("check_equal", check_equal)?; - // You can also accept variadic arguments to rust callbacks. + // You can also accept runtime variadic arguments to rust callbacks. let join = lua.create_function(|_, strings: Variadic| { // (This is quadratic!, it's just an example!) - Ok(strings.0.iter().fold("".to_owned(), |a, b| a + b)) + Ok(strings.iter().fold("".to_owned(), |a, b| a + b)) }); globals.set("join", join)?; @@ -139,9 +138,7 @@ fn examples() -> Result<()> { } } - let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| { - Ok(Vec2(x, y)) - }); + let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y))); globals.set("vec2", vec2_constructor)?; assert!( diff --git a/src/lua.rs b/src/lua.rs index 4af24eb..a121854 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -39,12 +39,10 @@ pub enum Value<'lua> { Function(Function<'lua>), /// Reference to a Lua thread (or coroutine). Thread(Thread<'lua>), - /// Reference to a userdata object that holds a custom type which implements - /// `UserData`. Special builtin userdata types will be represented as - /// other `Value` variants. + /// Reference to a userdata object that holds a custom type which implements `UserData`. + /// Special builtin userdata types will be represented as other `Value` variants. UserData(AnyUserData<'lua>), - /// `Error` is a special builtin userdata type. When received from Lua - /// it is implicitly cloned. + /// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned. Error(Error), } pub use self::Value::Nil; @@ -1518,7 +1516,7 @@ impl Lua { /// # } /// ``` /// - /// Use the `hlist_macro` crate to use multiple arguments: + /// Use tuples to accept multiple arguments: /// /// ``` /// # extern crate rlua; @@ -1674,25 +1672,26 @@ impl Lua { } } - pub fn from<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result> { + /// Converts a value that implements `ToLua` into a `Value` instance. + pub fn pack<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result> { t.to_lua(self) } - pub fn to<'lua, T: FromLua<'lua>>(&'lua self, value: Value<'lua>) -> Result { + /// Converts a `Value` instance into a value that implements `FromLua`. + pub fn unpack<'lua, T: FromLua<'lua>>(&'lua self, value: Value<'lua>) -> Result { T::from_lua(value, self) } - /// Packs up a value that implements `ToLuaMulti` into a `MultiValue` instance. - /// - /// This can be used to return arbitrary Lua values from a Rust function back to Lua. - pub fn pack<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> Result> { + /// Converts a value that implements `ToLuaMulti` into a `MultiValue` instance. + pub fn pack_multi<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> Result> { t.to_lua_multi(self) } - /// Unpacks a `MultiValue` instance into a value that implements `FromLuaMulti`. - /// - /// This can be used to convert the arguments of a Rust function called by Lua. - pub fn unpack<'lua, T: FromLuaMulti<'lua>>(&'lua self, value: MultiValue<'lua>) -> Result { + /// Converts a `MultiValue` instance into a value that implements `FromLuaMulti`. + pub fn unpack_multi<'lua, T: FromLuaMulti<'lua>>( + &'lua self, + value: MultiValue<'lua>, + ) -> Result { T::from_lua_multi(value, self) } diff --git a/src/multi.rs b/src/multi.rs index 1aed22d..53685ca 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -1,3 +1,5 @@ +use std::ops::{Deref, DerefMut}; +use std::iter::FromIterator; use std::result::Result as StdResult; use error::*; @@ -49,9 +51,45 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> { /// Can be used to pass variadic values to or receive variadic values from Lua, where the type of /// the values is all the same and the number of values is defined at runtime. This can be included -/// in an hlist when unpacking, but must be the final entry, and will consume the rest of the +/// in tuple when unpacking, but must be the final entry, and will consume the rest of the /// parameters given. -pub struct Variadic(pub Vec); +#[derive(Debug, Clone)] +pub struct Variadic(Vec); + +impl Variadic { + pub fn new() -> Variadic { + Variadic(Vec::new()) + } +} + +impl FromIterator for Variadic { + fn from_iter>(iter: I) -> Self { + Variadic(Vec::from_iter(iter)) + } +} + +impl IntoIterator for Variadic { + type Item = T; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Deref for Variadic { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Variadic { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic { fn to_lua_multi(self, lua: &'lua Lua) -> Result> { @@ -128,7 +166,7 @@ macro_rules! push_reverse { ($multi_value:expr,) => (); } -impl_tuple! { } +impl_tuple!{} impl_tuple! { A } impl_tuple! { A B } impl_tuple! { A B C } diff --git a/src/tests.rs b/src/tests.rs index 341d1e6..bad71bc 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -220,9 +220,7 @@ fn test_rust_function() { ).unwrap(); let lua_function = globals.get::<_, Function>("lua_function").unwrap(); - let rust_function = lua.create_function(|_, _: ()| { - Ok("hello") - }); + let rust_function = lua.create_function(|_, _: ()| Ok("hello")); globals.set("rust_function", rust_function).unwrap(); assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello"); @@ -303,13 +301,14 @@ fn test_metamethods() { methods.add_meta_function(MetaMethod::Sub, |_, (lhs, rhs): (MyUserData, MyUserData)| { Ok(MyUserData(lhs.0 - rhs.0)) }); - methods.add_meta_method(MetaMethod::Index, |_, data, index: LuaString| { - if index.to_str()? == "inner" { + methods.add_meta_method( + MetaMethod::Index, + |_, data, index: LuaString| if index.to_str()? == "inner" { Ok(data.0) } else { Err("no such custom index".to_lua_err()) - } - }); + }, + ); } } @@ -382,9 +381,9 @@ fn test_lua_multi() { assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar"); let (a, b) = mreturn.call::<_, (u64, u64)>(()).unwrap(); assert_eq!((a, b), (1, 2)); - let (a, b, Variadic(v)) = mreturn.call::<_, (u64, u64, Variadic)>(()).unwrap(); + let (a, b, v) = mreturn.call::<_, (u64, u64, Variadic)>(()).unwrap(); assert_eq!((a, b), (1, 2)); - assert_eq!(v, vec![3, 4, 5, 6]); + assert_eq!(v[..], [3, 4, 5, 6]); } #[test] From 8e9c93b9d16ee9a5a0ea2c0233cc4f667d78aa1f Mon Sep 17 00:00:00 2001 From: kyren Date: Tue, 1 Aug 2017 13:57:51 -0400 Subject: [PATCH 4/5] Formatting, only implement tuple conversion up to 12 --- src/multi.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/multi.rs b/src/multi.rs index 53685ca..50dc63a 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -167,19 +167,15 @@ macro_rules! push_reverse { } impl_tuple!{} -impl_tuple! { A } -impl_tuple! { A B } -impl_tuple! { A B C } -impl_tuple! { A B C D } -impl_tuple! { A B C D E } -impl_tuple! { A B C D E F } -impl_tuple! { A B C D E F G } -impl_tuple! { A B C D E F G H } -impl_tuple! { A B C D E F G H I } -impl_tuple! { A B C D E F G H I J } -impl_tuple! { A B C D E F G H I J K } -impl_tuple! { A B C D E F G H I J K L } -impl_tuple! { A B C D E F G H I J K L M } -impl_tuple! { A B C D E F G H I J K L M N } -impl_tuple! { A B C D E F G H I J K L M N O } -impl_tuple! { A B C D E F G H I J K L M N O P } +impl_tuple!{A} +impl_tuple!{A B} +impl_tuple!{A B C} +impl_tuple!{A B C D} +impl_tuple!{A B C D E} +impl_tuple!{A B C D E F} +impl_tuple!{A B C D E F G} +impl_tuple!{A B C D E F G H} +impl_tuple!{A B C D E F G H I} +impl_tuple!{A B C D E F G H I J} +impl_tuple!{A B C D E F G H I J K} +impl_tuple!{A B C D E F G H I J K L} From f44917ff83b86d0a7136867a613abadba83e1ed3 Mon Sep 17 00:00:00 2001 From: kyren Date: Tue, 1 Aug 2017 14:09:47 -0400 Subject: [PATCH 5/5] Replace _: () with () everywhere --- examples/examples.rs | 2 +- src/tests.rs | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/examples.rs b/examples/examples.rs index e045b98..51c21a6 100644 --- a/examples/examples.rs +++ b/examples/examples.rs @@ -127,7 +127,7 @@ fn examples() -> Result<()> { impl UserData for Vec2 { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("magnitude", |_, vec, _: ()| { + methods.add_method("magnitude", |_, vec, ()| { let mag_squared = vec.0 * vec.0 + vec.1 * vec.1; Ok(mag_squared.sqrt()) }); diff --git a/src/tests.rs b/src/tests.rs index bad71bc..0706e2e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -220,7 +220,7 @@ fn test_rust_function() { ).unwrap(); let lua_function = globals.get::<_, Function>("lua_function").unwrap(); - let rust_function = lua.create_function(|_, _: ()| Ok("hello")); + let rust_function = lua.create_function(|_, ()| Ok("hello")); globals.set("rust_function", rust_function).unwrap(); assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello"); @@ -254,7 +254,7 @@ fn test_methods() { impl UserData for MyUserData { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("get_value", |_, data, _: ()| Ok(data.0)); + methods.add_method("get_value", |_, data, ()| Ok(data.0)); methods.add_method_mut("set_value", |_, data, args| { data.0 = args; Ok(()) @@ -294,7 +294,7 @@ fn test_metamethods() { impl UserData for MyUserData { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("get", |_, data, _: ()| Ok(data.0)); + methods.add_method("get", |_, data, ()| Ok(data.0)); methods.add_meta_function(MetaMethod::Add, |_, (lhs, rhs): (MyUserData, MyUserData)| { Ok(MyUserData(lhs.0 + rhs.0)) }); @@ -481,7 +481,7 @@ fn test_error() { ).unwrap(); let rust_error_function = - lua.create_function(|_, _: ()| -> Result<()> { Err(TestError.to_lua_err()) }); + lua.create_function(|_, ()| -> Result<()> { Err(TestError.to_lua_err()) }); globals .set("rust_error_function", rust_error_function) .unwrap(); @@ -540,7 +540,7 @@ fn test_error() { "#, None, )?; - let rust_panic_function = lua.create_function(|_, _: ()| -> Result<()> { + let rust_panic_function = lua.create_function(|_, ()| -> Result<()> { panic!("expected panic, this panic should be caught in rust") }); globals.set("rust_panic_function", rust_panic_function)?; @@ -566,7 +566,7 @@ fn test_error() { "#, None, )?; - let rust_panic_function = lua.create_function(|_, _: ()| -> Result<()> { + let rust_panic_function = lua.create_function(|_, ()| -> Result<()> { panic!("expected panic, this panic should be caught in rust") }); globals.set("rust_panic_function", rust_panic_function)?; @@ -723,12 +723,12 @@ fn test_result_conversions() { let lua = Lua::new(); let globals = lua.globals(); - let err = lua.create_function(|_, _: ()| { + let err = lua.create_function(|_, ()| { Ok(Err::( "only through failure can we succeed".to_lua_err(), )) }); - let ok = lua.create_function(|_, _: ()| Ok(Ok::<_, Error>("!".to_owned()))); + let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned()))); globals.set("err", err).unwrap(); globals.set("ok", ok).unwrap(); @@ -780,7 +780,7 @@ fn test_expired_userdata() { impl UserData for MyUserdata { fn add_methods(methods: &mut UserDataMethods) { - methods.add_method("access", |_, this, _: ()| { + methods.add_method("access", |_, this, ()| { assert!(this.id == 123); Ok(()) }); @@ -869,7 +869,7 @@ fn string_views() { #[test] fn coroutine_from_closure() { let lua = Lua::new(); - let thrd_main = lua.create_function(|_, _: ()| Ok(())); + let thrd_main = lua.create_function(|_, ()| Ok(())); lua.globals().set("main", thrd_main).unwrap(); let thrd: Thread = lua.eval("coroutine.create(main)", None).unwrap(); thrd.resume::<_, ()>(()).unwrap(); @@ -879,7 +879,7 @@ fn coroutine_from_closure() { #[should_panic] fn coroutine_panic() { let lua = Lua::new(); - let thrd_main = lua.create_function(|lua, _: ()| { + let thrd_main = lua.create_function(|lua, ()| { // whoops, 'main' has a wrong type let _coro: u32 = lua.globals().get("main").unwrap(); Ok(())