Permalink
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up| use std::f32; | |
| use std::iter::FromIterator; | |
| use rlua::{Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic}; | |
| fn main() -> Result<()> { | |
| // You can create a new Lua state with `Lua::new()`. This loads the default Lua std library | |
| // *without* the debug library. You can get more control over this with the other | |
| // `Lua::xxx_new_xxx` functions. | |
| let lua = Lua::new(); | |
| // In order to interact with Lua values at all, you must do so inside a callback given to the | |
| // `Lua::context` method. This provides some extra safety and allows the rlua API to avoid some | |
| // extra runtime checks. | |
| lua.context(|lua_ctx| { | |
| // You can get and set global variables. Notice that the globals table here is a permanent | |
| // reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is | |
| // based heavily around sharing and internal mutation (just like Lua itself). | |
| let globals = lua_ctx.globals(); | |
| globals.set("string_var", "hello")?; | |
| globals.set("int_var", 42)?; | |
| Ok(()) | |
| })?; | |
| lua.context(|lua_ctx| { | |
| // The Lua state lives inside the top-level `Lua` value, and all state changes persist | |
| // between `Lua::context` calls. This is another table reference in another context call, | |
| // but it refers to the same table _G. | |
| let globals = lua_ctx.globals(); | |
| assert_eq!(globals.get::<_, String>("string_var")?, "hello"); | |
| assert_eq!(globals.get::<_, i64>("int_var")?, 42); | |
| Ok(()) | |
| })?; | |
| lua.context(|lua_ctx| { | |
| let globals = lua_ctx.globals(); | |
| // You can load and evaluate Lua code. The returned type of `Context::load` is a builder | |
| // that allows you to change settings before running Lua code. Here, we are using it to set | |
| // the name of the laoded chunk to "example code", which will be used when Lua error | |
| // messages are printed. | |
| lua_ctx | |
| .load( | |
| r#" | |
| global = 'foo'..'bar' | |
| "#, | |
| ) | |
| .set_name("example code")? | |
| .exec()?; | |
| assert_eq!(globals.get::<_, String>("global")?, "foobar"); | |
| assert_eq!(lua_ctx.load("1 + 1").eval::<i32>()?, 2); | |
| assert_eq!(lua_ctx.load("false == false").eval::<bool>()?, true); | |
| assert_eq!(lua_ctx.load("return 1 + 2").eval::<i32>()?, 3); | |
| // You can create and manage Lua tables | |
| let array_table = lua_ctx.create_table()?; | |
| array_table.set(1, "one")?; | |
| array_table.set(2, "two")?; | |
| array_table.set(3, "three")?; | |
| assert_eq!(array_table.len()?, 3); | |
| let map_table = lua_ctx.create_table()?; | |
| map_table.set("one", 1)?; | |
| map_table.set("two", 2)?; | |
| map_table.set("three", 3)?; | |
| let v: i64 = map_table.get("two")?; | |
| assert_eq!(v, 2); | |
| // You can pass values like `Table` back into Lua | |
| globals.set("array_table", array_table)?; | |
| globals.set("map_table", map_table)?; | |
| lua_ctx | |
| .load( | |
| r#" | |
| for k, v in pairs(array_table) do | |
| print(k, v) | |
| end | |
| for k, v in pairs(map_table) do | |
| print(k, v) | |
| end | |
| "#, | |
| ) | |
| .exec()?; | |
| // You can load Lua functions | |
| let print: Function = globals.get("print")?; | |
| print.call::<_, ()>("hello from rust")?; | |
| // This API generally handles variadics using tuples. This is one way to call a function with | |
| // multiple parameters: | |
| print.call::<_, ()>(("hello", "again", "from", "rust"))?; | |
| // But, you can also pass variadic arguments with the `Variadic` type. | |
| print.call::<_, ()>(Variadic::from_iter( | |
| ["hello", "yet", "again", "from", "rust"].iter().cloned(), | |
| ))?; | |
| // You can bind rust functions to Lua as well. Callbacks receive a `Context` 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 parameters given by Lua, in | |
| // this case, the function expects two string sequences. | |
| let check_equal = | |
| lua_ctx.create_function(|_, (list1, list2): (Vec<String>, Vec<String>)| { | |
| // 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 runtime variadic arguments to rust callbacks. | |
| let join = lua_ctx.create_function(|_, strings: Variadic<String>| { | |
| // (This is quadratic!, it's just an example!) | |
| Ok(strings.iter().fold("".to_owned(), |a, b| a + b)) | |
| })?; | |
| globals.set("join", join)?; | |
| assert_eq!( | |
| lua_ctx | |
| .load(r#"check_equal({"a", "b", "c"}, {"a", "b", "c"})"#) | |
| .eval::<bool>()?, | |
| true | |
| ); | |
| assert_eq!( | |
| lua_ctx | |
| .load(r#"check_equal({"a", "b", "c"}, {"d", "e", "f"})"#) | |
| .eval::<bool>()?, | |
| false | |
| ); | |
| assert_eq!( | |
| lua_ctx.load(r#"join("a", "b", "c")"#).eval::<String>()?, | |
| "abc" | |
| ); | |
| // Callbacks receive a context value as their first parameter so that they can use it to | |
| // create new Lua values, if necessary. Often times this is not necessary and the context | |
| // parameter can be ignored. | |
| let create_table = lua_ctx.create_function(|lua_ctx, ()| { | |
| let t = lua_ctx.create_table()?; | |
| t.set(1, 1)?; | |
| t.set(2, 2)?; | |
| Ok(t) | |
| })?; | |
| globals.set("create_table", create_table)?; | |
| assert_eq!(lua_ctx.load(r#"create_table()[2]"#).eval::<i32>()?, 2); | |
| // You can create userdata with methods and metamethods defined on them. | |
| // Here's a worked example that shows many of the features of this API | |
| // together | |
| #[derive(Copy, Clone)] | |
| struct Vec2(f32, f32); | |
| impl UserData for Vec2 { | |
| fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { | |
| methods.add_method("magnitude", |_, vec, ()| { | |
| let mag_squared = vec.0 * vec.0 + vec.1 * vec.1; | |
| Ok(mag_squared.sqrt()) | |
| }); | |
| methods.add_meta_function(MetaMethod::Add, |_, (vec1, vec2): (Vec2, Vec2)| { | |
| Ok(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1)) | |
| }); | |
| } | |
| } | |
| let vec2_constructor = lua_ctx.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y)))?; | |
| globals.set("vec2", vec2_constructor)?; | |
| assert!( | |
| (lua_ctx | |
| .load("(vec2(1, 2) + vec2(2, 2)):magnitude()") | |
| .eval::<f32>()? | |
| - 5.0) | |
| .abs() | |
| < f32::EPSILON | |
| ); | |
| // Normally, Rust types passed to `Lua` must be `Send`, because `Lua` itself is `Send`, and | |
| // must be `'static`, because there is no way to be sure of their lifetime inside the Lua | |
| // state. There is, however, a limited way to lift both of these requirements. You can | |
| // call `Context::scope` to create userdata and callbacks types that only live for as long | |
| // as the call to scope, but do not have to be `Send` OR `'static`. | |
| { | |
| let mut rust_val = 0; | |
| lua_ctx.scope(|scope| { | |
| // We create a 'sketchy' Lua callback that holds a mutable reference to the variable | |
| // `rust_val`. Outside of a `Context::scope` call, this would not be allowed | |
| // because it could be unsafe. | |
| lua_ctx.globals().set( | |
| "sketchy", | |
| scope.create_function_mut(|_, ()| { | |
| rust_val = 42; | |
| Ok(()) | |
| })?, | |
| )?; | |
| lua_ctx.load("sketchy()").exec() | |
| })?; | |
| assert_eq!(rust_val, 42); | |
| } | |
| // We were able to run our 'sketchy' function inside the scope just fine. However, if we | |
| // try to run our 'sketchy' function outside of the scope, the function we created will have | |
| // been invalidated and we will generate an error. If our function wasn't invalidated, we | |
| // might be able to improperly access the freed `rust_val` which would be unsafe. | |
| assert!(lua_ctx.load("sketchy()").exec().is_err()); | |
| Ok(()) | |
| }) | |
| } |