8个版本

使用旧的Rust 2015

0.1.10 2023年2月5日
0.1.9 2022年5月22日

84#lua

每月23次下载

MIT/Apache

335KB
7K SLoC

C 4.5K SLoC // 0.2% comments Lua 1.5K SLoC // 0.2% comments Rust 1.5K SLoC // 0.0% comments

td_rlua

此库是Lua 5.3的高级绑定。您无法访问Lua堆栈,您所能做的只是读取/写入变量(包括回调)并执行Lua代码。

Build Status

如何安装?

将以下内容添加到您的项目中的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);

可以使用setquery读取和写入Lua上下文的全局变量。函数query返回一个Option<T>并复制值。

可以读取和写入的基本类型有:i8i16i32u8u16u32f32f64boolString。可以写入但不可以读取&str

如果您愿意,您还可以通过实现LuaPushLuaRead特质来添加其他类型。

执行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 trait提供的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_userdata,它将操作对象的引用,如果使用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,可以使用它。

贡献

欢迎贡献!

依赖项

~625KB
~16K SLoC