Examples and initial README
This commit is contained in:
parent
065c69894a
commit
c7693ae0a2
68
README.md
Normal file
68
README.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
# rlua -- High level bindings between Rust and Lua
|
||||
|
||||
This library is a WIP high level interface between Rust and Lua. Its major goal
|
||||
is to expose as flexible of an API between Rust and Lua as possible, while also
|
||||
being completely safe.
|
||||
|
||||
There are other high level lua bindings systems for rust, and this crate is an
|
||||
exploration of a different part of the design space. The main high level
|
||||
interface to Lua right now is [hlua](https://github.com/tomaka/hlua/) which you
|
||||
should definitely check out and use if it suits your needs. This crate has the
|
||||
following differences with hlua:
|
||||
|
||||
* Handles to Lua values use the Lua registry, not the stack
|
||||
* Handles to Lua values are all internally mutable
|
||||
* Handles to Lua values use non-mutable borrows the main Lua object, so
|
||||
there can be multiple handles or long lived handles
|
||||
* Targets lua 5.3
|
||||
|
||||
The key difference here is that rlua handles rust-side references to Lua values
|
||||
in a fundamentally different way than hlua, more similar to other lua bindings
|
||||
systems like (Selene)[https://github.com/jeremyong/Selene] for C++. Values like
|
||||
LuaTable and LuaFunction that hold onto Lua values in the Rust stack, instead of
|
||||
pointing at values in the Lua stack, are placed into the registry with luaL_ref.
|
||||
In this way, it is possible to have an arbitrary number of handles to internal
|
||||
Lua values at any time, created and destroyed in arbitrary order. This approach
|
||||
IS slightly slower than the approach that hlua takes of only manipulating the
|
||||
lua stack, but this, combined with internal mutability, allows for a much more
|
||||
flexible API.
|
||||
|
||||
Currently exposes a *somewhat* complete Lua API covering values and tables and
|
||||
functions and userdata, but does not yet cover coroutines. This API is actually
|
||||
heavily inspired by the lua API that I previously wrote for Starbound, and will
|
||||
become feature complete with that API over time. Some capabilities that API has
|
||||
that are on the roadmap:
|
||||
|
||||
* Proper coroutine support
|
||||
* Lua profiling support
|
||||
* Execution limits like total instruction limits or lua <-> rust recursion
|
||||
limits
|
||||
* Security limits on the lua stdlib, and general control over the loaded
|
||||
lua libraries.
|
||||
* "Context" or "Sandboxing" support, this was probably a bit too heavyweight
|
||||
in Starbound's API, but there will be the ability to set the _ENV upvalue
|
||||
of a loaded chunk to a table other than _G, so that you can have different
|
||||
environments for different loaded chunks.
|
||||
|
||||
There are also some more general things that need to be done:
|
||||
|
||||
* More fleshed out Lua API, things like Table metatables and exposing the
|
||||
registry.
|
||||
* MUCH better API documentation, the current API documentation is basically
|
||||
non-existent.
|
||||
* Performance testing.
|
||||
|
||||
Additionally, there are ways I would like to change this API, once support lands
|
||||
in rustc. For example:
|
||||
|
||||
* Once ATCs land, there should be a way to wrap callbacks based on argument
|
||||
and return signature, rather than calling lua.pack / lua.unpack inside the
|
||||
callback. Until then, it is impossible to name the type of the function
|
||||
that would do the wrapping, see (this reddit
|
||||
discussion)[https://www.reddit.com/r/rust/comments/5yujt6/a_very_complex_lifetime_problem_possibly_a/]
|
||||
* Once tuple based variadic generics land, the plan is to completely
|
||||
eliminate the lua multi macros in favor of simple tuples.
|
||||
|
||||
## Examples
|
||||
|
||||
Please look at the (examples)[examples/examples.rs]
|
134
examples/examples.rs
Normal file
134
examples/examples.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
#[macro_use]
|
||||
extern crate rlua;
|
||||
|
||||
use rlua::*;
|
||||
|
||||
fn examples() -> LuaResult<()> {
|
||||
// Create a Lua context with Lua::new(). Eventually, this will allow further control on the
|
||||
// lua std library, and will specifically allow limiting Lua to a subset of "safe"
|
||||
// functionality.
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
// You can get and set global variables
|
||||
|
||||
lua.set("string_var", "hello")?;
|
||||
lua.set("int_var", 42)?;
|
||||
|
||||
assert_eq!(lua.get::<_, String>("string_var")?, "hello");
|
||||
assert_eq!(lua.get::<_, i64>("int_var")?, 42);
|
||||
|
||||
// You can load and evaluate lua code. The second parameter here gives the chunk a better name
|
||||
// when lua error messages are printed.
|
||||
|
||||
lua.load(r#"
|
||||
global = 'foo'..'bar'
|
||||
"#,
|
||||
Some("example code"))?;
|
||||
assert_eq!(lua.get::<_, String>("global")?, "foobar");
|
||||
|
||||
assert_eq!(lua.eval::<i32>("1 + 1")?, 2);
|
||||
assert_eq!(lua.eval::<bool>("false == false")?, true);
|
||||
assert_eq!(lua.eval::<i32>("return 1 + 2")?, 3);
|
||||
|
||||
// You can create and manage lua tables
|
||||
|
||||
let array_table = lua.create_empty_table()?;
|
||||
array_table.set(1, "one")?;
|
||||
array_table.set(2, "two")?;
|
||||
array_table.set(3, "three")?;
|
||||
assert_eq!(array_table.length()?, 3);
|
||||
|
||||
let map_table = lua.create_empty_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 LuaTable back into Lua
|
||||
|
||||
lua.set("array_table", array_table)?;
|
||||
lua.set("map_table", map_table)?;
|
||||
|
||||
lua.eval::<()>(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
|
||||
"#)?;
|
||||
|
||||
// You can load lua functions
|
||||
|
||||
let print: LuaFunction = lua.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
|
||||
// There is a specific method for handling variadics that involves Heterogeneous Lists. This
|
||||
// is one way to call a function with multiple parameters:
|
||||
|
||||
print
|
||||
.call::<_, ()>(lua_multi!["hello", "again", "from", "rust"])?;
|
||||
|
||||
// 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 LuaMultiValue. The
|
||||
// first thing that most wrapped functions will do is "unpack" this LuaMultiValue 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 lua_multi_pat![list1, list2] = lua.unpack::<LuaMulti![Vec<String>, Vec<String>]>(args)?;
|
||||
|
||||
// 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 LuaMultiValue. 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)
|
||||
})?;
|
||||
|
||||
lua.set("check_equal", check_equal)?;
|
||||
|
||||
assert_eq!(lua.eval::<bool>(r#"check_equal({"a", "b", "c"}, {"a", "b", "c"})"#)?,
|
||||
true);
|
||||
assert_eq!(lua.eval::<bool>(r#"check_equal({"a", "b", "c"}, {"d", "e", "f"})"#)?,
|
||||
false);
|
||||
|
||||
// You can create userdata with methods and metamethods defined on them. Here's a more
|
||||
// complete example that shows all of the features of this API together
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vec2(f32, f32);
|
||||
|
||||
impl LuaUserDataType for Vec2 {
|
||||
fn add_methods(methods: &mut LuaUserDataMethods<Self>) {
|
||||
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(LuaMetaMethod::Add, |lua, params| {
|
||||
let lua_multi_pat![vec1, vec2] = lua.unpack::<LuaMulti![Vec2, Vec2]>(params)?;
|
||||
lua.pack(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let vec2_constructor = lua.create_function(|lua, args| {
|
||||
let lua_multi_pat![x, y] = lua.unpack::<LuaMulti![f32, f32]>(args)?;
|
||||
lua.pack(Vec2(x, y))
|
||||
})?;
|
||||
lua.set("vec2", vec2_constructor)?;
|
||||
|
||||
assert_eq!(lua.eval::<f32>("(vec2(1, 2) + vec2(2, 2)):magnitude()")?,
|
||||
5.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
examples().unwrap();
|
||||
}
|
Loading…
Reference in a new issue