Rust
#[derive(LuaMarshalling)]
pub struct A {
string: String,
integer: i32,
}
pub mod extern_ffi {
pub fn make_a(string: &str, integer: i32) -> A {
A {
string: string.to_owned(),
integer,
}
}
pub fn describe(a: A) -> String {
format!("A: {:?}", a)
}
}Lua
local example = require('rust-example')
local a = example.make_a("Test string", 42)
print("a", a.string, a.integer)
print("describe", example.describe(a))- Supported Rust types include primitives,
Vec,Option,Stringand customstructwithderive(LuaMarshalling)and any combination of those.&stris supported only as an argument but is faster thanString.&[]is supported only for primitive types.Resultis supported only as a return argument. OptionsNoneisnilin Lua.- Only
&strand&[]of primitive types are passed as references to Rust, all other types are copied. - A Rust
structis converted to a Luatable, but can still be used as an argument. For this to work, the Lua table also keeps a reference to the native object pointer. - The native object pointer is garbage collected by calling back to Rust.
To keep the native pointer and the table consistent, the
tableis immutable.
- Passing a Lua string to Rust as
&strorStringmay fail with anerrordue to UTF-8 requirements. However, passing a Lua string to Rust as a&[u8]orVec<u8>will not. - Returning a string from Rust may fail as well with an
errordue to strings containing the zero-byte. - A Rust
panicwill cause anerrorin Lua.
Vec<Option<T>>have been disabled. Lua arrays generally do not handlenullvalues well. See www.lua.org/pil/19.1.html for more information.structtypenames must be unique. Separate modules are not enough.- Identifiers can not be Lua or C reserved keywords. For example, a variable cannot be called
short. - The
__prefix is reserved for hidden identifiers and should not be used as field names or function arguments.
cargo new --lib example_setup- In
example_setupcreate the filesrc/build.rswith the following content
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn main() {
let rust_output = Path::new(&env::var("OUT_DIR").unwrap()).join("ffi.rs");
let output = generator::generate(
&env::current_dir().unwrap().as_path().join("src/lib.rs"),
"fuzzy_filter_lua_ffi",
false,
);
File::create(rust_output.clone())
.unwrap()
.write_all(output.as_bytes())
.unwrap();
assert!(rust_output.exists());
}Note the library_name parameter to generator::generator must be equal to the library name of the crate.
Add the following to the Cargo.toml under [package]
build = "src/build.rs"Under [dependencies] add the following
libc = "0.2.20"
c-marshalling = { git = "https://github.com/distil/rust_lua_ffi" }
lua-marshalling = { git = "https://github.com/distil/rust_lua_ffi" }Add the following section to the Cargo.toml as well
[build-dependencies]
generator = { git = "https://github.com/distil/rust_lua_ffi" }
[lib]
crate-type = ["cdylib"]In src/lib.rs add the following
pub mod extern_ffi {
pub fn hello_world() -> String {
"Hello World!".to_owned()
}
}
include!(concat!(env!("OUT_DIR"), "/ffi.rs"));After the library has been built, the Lua interface code can be generated using the following command
LD_LIBRARY_PATH=..path-to-example_setup/target/debug/ \
RUST_LUA_FFI_TYPE_PREFIX=example_setup \
luajit ..path-to-rust_lua_ffi/lua/bootstrap.lua example_setup > api.luaNote the setting of RUST_LUA_FFI_TYPE_PREFIX to the module name. This is
optional unless you need to use separately generated bindings for multiple Rust
libraries in the same Lua instance.
To use the api.lua file generated in the Building step, create a Lua file called example.lua in the same directory as the Lua interface code containing
local api = require('api')
print(api.hello_world())Execute the file using the following command
LD_LIBRARY_PATH=..path-to-example_setup/target/debug/ luajit example.lua