3 个版本
使用旧 Rust 2015
0.1.3 | 2022年5月13日 |
---|---|
0.1.2 | 2016年12月1日 |
0.1.0 | 2016年3月31日 |
#105 in #lua
45 每月下载量
在 8 个crate中使用 (直接使用 3 个)
565KB
15K SLoC
td_rlua
此库是 Lua 5.3 的高级绑定。您无法访问 Lua 栈,您所能做的就是读写变量(包括回调)和执行 Lua 代码。
如何安装它?
将以下内容添加到您项目的 Cargo.toml
文件中
[dependencies]
td_rlua = "0.1.0"
如何使用它?
extern crate td_rlua;
use td_rlua::Lua;
Lua
结构体是这个库的主要元素。它代表了一个可以执行 Lua 代码的上下文。
let mut lua = Lua::new(); // mutable is mandatory
读写变量
lua.set("x", 2);
let _: () = lua.exec_string("x = x + 1").unwrap();
let x: i32 = lua.query("x").unwrap();
assert_eq!(x, 3);
可以使用 set
和 query
函数读写 Lua 上下文的全局变量。函数 query
返回一个 Option<T>
并复制值。
可以读写的基本类型包括:i8
、i16
、i32
、u8
、u16
、u32
、f32
、f64
、bool
、String
。可以写入但不能读取 &str
。
如果您愿意,也可以通过实现 LuaPush
和 LuaRead
特性来添加其他类型。
执行 Lua
let x: u32 = lua.exec_string("return 6 * 2;").unwrap(); // equals 12
exec_string
函数接受一个 &str
并返回一个 Option<T>
,其中 T: LuaRead
。
编写函数
为了编写函数,您必须将其包装在 td_rlua::functionX
中,其中 X
是参数的数量。这是目前 Rust 的推断系统的一个限制。
fn add(a: i32, b: i32) -> i32 {
a + b
}
lua.set("add", td_rlua::function2(add));
let _: () = lua.exec_string("c = add(2, 4)").unwrap(); // calls the `add` function above
let c: i32 = lua.query("c").unwrap();
assert_eq!(c, 6);
在 Lua 中,函数与常规变量完全相同。
您可以编写常规函数以及闭包。
lua.set("mul", td_rlua::function2(|a: i32, b: i32| a * b));
请注意,Lua 上下文的寿命必须等于或短于闭包的寿命。这将在编译时强制执行。
let mut a = 5i;
{
let mut lua = Lua::new();
lua.set("inc", || a += 1); // borrows 'a'
for i in (0 .. 15) {
let _: () = lua.exec_string("inc()").unwrap();
}
} // unborrows `a`
assert_eq!(a, 20)
错误处理
extern "C" fn error_handle(lua: *mut c_lua::lua_State) -> libc::c_int {
let err = unsafe { c_lua::lua_tostring(lua, -1) };
let err = unsafe { CStr::from_ptr(err) };
let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
println!("error:{}", err);
0
}
lua.register("error_handle", error_handle);
exec_string
的默认操作将调用 pcall,并将错误函数 _G["error_handle"] 设置为默认值,这样您就可以将 'error_handle' 函数重置为您的自定义函数。
操作 Lua 表
可以通过读取 LuaTable
对象来操作 Lua 表。这可以通过读取一个 LuaTable
对象轻松实现。
let _:() = lua.exec_string("a = { 9, 8, 7 }").unwrap();
let mut table : LuaTable = lua.query("a").unwrap();
let x: i32 = table.query(2).unwrap();
assert_eq!(x, 8);
table.set(3, "hello");
let y: String = table.query(3).unwrap();
assert_eq!(y, "hello");
let z: i32 = table.query(1).unwrap();
assert_eq!(z, 9);
您可以使用 .iter()
函数遍历表格。请注意,迭代器返回的值是一个 Option<(Key, Value)>
,当键或值不能转换为请求的类型时,Option
为空。在处理此类问题时,标准 Iterator
特性提供的 filter_map
函数非常有用。
let _:() = lua.exec_string("a = { 9, 8, 7 }").unwrap();
let mut table : LuaTable = lua.query("a").unwrap();
for _ in 0 .. 10 {
let table_content: Vec<Option<(u32, u32)>> = table.iter().collect();
assert_eq!(table_content, vec![ Some((1,9)), Some((2,8)), Some((3,7)) ]);
}
用户数据
当您将函数暴露给 Lua 时,您可能希望读取或写入更复杂的数据对象。这被称为 用户数据。
为此,您应该为您的数据类型实现 LuaPush
。这通常是通过调用 userdata::push_userdata
来完成的。如果您使用 userdata::push_lightuserdata
,则会复制一次用户数据,以便 Lua GC 管理器使用;如果您使用 userdata::push_lightuserdata
,则由 Rust 管理用户数据的生命周期,因此不会进行复制。
#[derive(Clone, Debug)]
struct Foo {
a : i32,
};
impl<'a> td_rlua::LuaPush for &'a mut Foo {
fn push_to_lua(self, lua: *mut c_lua::lua_State) -> i32 {
td_rlua::userdata::push_userdata(self, lua, |_|{})
}
}
impl<'a> td_rlua::LuaRead for &'a mut Foo {
fn lua_read_at_position(lua: *mut c_lua::lua_State, index: i32) -> Option<&'a mut Foo> {
td_rlua::userdata::read_userdata(lua, index)
}
}
let xx = &mut Foo {
a : 10,
};
lua.set("a", xx);
let get: &mut Foo = lua.query("a").unwrap();
assert!(get.a == 10);
get.a = 100;
let get: &mut Foo = lua.query("a").unwrap();
assert!(get.a == 100);
使用 lightuserdata 可以更改
impl<'a> td_rlua::LuaPush for &'a mut Foo {
fn push_to_lua(self, lua: *mut c_lua::lua_State) -> i32 {
td_rlua::userdata::push_lightuserdata(self, lua, |_|{})
}
}
自定义 Lua 调用的用户数据需要实现 NewStruct
#[derive(Clone, Debug)]
struct TestLuaSturct {
index : i32,
}
impl NewStruct for TestLuaSturct {
fn new() -> TestLuaSturct {
println!("new !!!!!!!!!!!!!!");
TestLuaSturct {
index : 19,
}
}
fn name() -> &'static str {
"TestLuaSturct"
}
}
impl<'a> LuaRead for &'a mut TestLuaSturct {
fn lua_read_at_position(lua: *mut c_lua::lua_State, index: i32) -> Option<&'a mut TestLuaSturct> {
td_rlua::userdata::read_userdata(lua, index)
}
}
现在我们可以自定义函数
let mut lua = Lua::new();
lua.openlibs();
fn one_arg(obj : &mut TestLuaSturct) -> i32 { obj.index = 10; 5 };
fn two_arg(obj : &mut TestLuaSturct, index : i32) { obj.index = index;};
let mut value = td_rlua::LuaStruct::<TestLuaSturct>::new(lua.state());
value.create().def("one_arg", td_rlua::function1(one_arg)).def("two_arg", td_rlua::function2(two_arg));
let _ : Option<()> = lua.exec_string("x = TestLuaSturct()");
let val : Option<i32> = lua.exec_string("return x:one_arg()");
assert_eq!(val, Some(5));
let obj : Option<&mut TestLuaSturct> = lua.query("x");
assert_eq!(obj.unwrap().index, 10);
let val : Option<i32> = lua.exec_string("x:two_arg(121)");
assert_eq!(val, None);
let obj : Option<&mut TestLuaSturct> = lua.query("x");
assert_eq!(obj.unwrap().index, 121);
let obj : Option<&mut TestLuaSturct> = lua.exec_string("return TestLuaSturct()");
assert_eq!(obj.unwrap().index, 19);
热修复
在运行时,如果我们需要更改某些逻辑,我们需要重新启动进程,这可能会丢失一些内存数据,因此有时我们需要更新逻辑,同时保留内存数据,因此我们需要热修复。
let mut lua = Lua::new();
lua.openlibs();
lua.enable_hotfix();
let _ = lua.exec_func2("hotfix", r"
local value = {3, 4}
function get_a()
value[2] = 3
return value[1]
end
function get_b()
return value[2]
end
", "hotfix");
参考
该项目参考 hlua,如果您使用 lua5.2,则可以使用它。
贡献
欢迎贡献!