7个版本
新 0.9.0-beta.4 | 2024年8月22日 |
---|---|
0.9.0-beta.2 | 2023年5月20日 |
0.8.0 | 2022年6月9日 |
0.8.0-beta.5-3 | 2022年5月28日 |
0.8.0-beta.5-2 | 2022年5月25日 |
#101 in 异步
每月 27 次下载
755KB
16K SLoC
mlua
注意
请参阅 v0.8 分支以获取crates.io上发布的
mlua
的稳定版本。
mlua
是针对Lua编程语言的Rust绑定,旨在提供安全(尽可能做到),高级、易于使用、实用且灵活的API。
作为rlua
的分支,mlua
支持Lua 5.4、5.3、5.2、5.1(包括LuaJIT)和Roblox Luau,并允许在Rust中编写本地Lua模块以及以独立模式使用Lua。
这是一个增加了Factorio Lua支持的分支。
mlua
在Windows/macOS/Linux上进行了测试,包括在x86_64
平台上的模块模式和GitHub Actions上的交叉编译到aarch64
(也支持其他目标)。
使用方法
功能标志
mlua
使用功能标志来减少依赖项、编译代码的数量,并允许选择仅需要的功能集。以下是可用功能标志的列表。默认情况下,mlua
不启用任何功能。
lua54
:激活Lua 5.4 支持lua53
:激活Lua 5.3 支持lua52
:激活 Lua 5.2 支持lua51
:激活 Lua 5.1 支持luajit
:激活 LuaJIT 支持luajit52
:激活 LuaJIT 支持,与 Lua 5.2 部分兼容luau
:激活 Luau 支持(自动 vendored 模式)lua-factorio
:激活 Factorio Lua 支持(自动 vendored 模式)vendored
:在mlua
编译时,从源码构建静态 Lua(JIT) 库,使用 lua-src 或 luajit-src cratesmodule
:启用模块模式(为 Lua 构建可加载的cdylib
库)async
:启用 async/await 支持(可以使用任何执行器,例如 tokio 或 async-std)send
:使mlua::Lua
能够跨线程边界传输(向mlua::Function
和mlua::UserData
添加Send
要求)serialize
:使用 serde 框架向mlua
类型添加序列化和反序列化支持macros
:启用过程宏(例如chunk!
)parking_lot
:支持由 parking_lot 的原语(Arc<Mutex>
和Arc<RwLock>
)包裹的 UserData 类型unstable
:启用 不稳定 功能。这些功能的公共 API 在不同版本之间可能会破坏。
Async/await 支持
mlua
支持所有 Lua 版本的 async/await,包括 Luau。
这通过 Lua 协程 实现,并需要运行 Thread 以及在 Cargo.toml
中启用 feature = "async"
示例:
序列化(serde)支持
启用 serialize
特性标志后,mlua
允许您将实现 serde::Serialize
和 serde::Deserialize
的任何类型序列化和反序列化为 mlua::Value
。此外,mlua
还为其提供 serde::Serialize
特性实现(包括对 UserData
的支持)。
编译
您必须启用以下功能之一: lua54
、lua53
、lua52
、lua51
、luajit(52)
或 luau
,具体取决于所选择的 Lua 版本。
默认情况下,mlua
使用 pkg-config
工具查找所选 Lua 版本的 lua 包含文件和库。在大多数情况下,它按预期工作,尽管有时可能更倾向于使用自定义 lua 库。为了实现这一点,mlua 支持以下环境变量:LUA_INC
、LUA_LIB
、LUA_LIB_NAME
和 LUA_LINK
。《code>LUA_LINK 是可选的,可以是 dylib
(动态库)或 static
(静态库,.a
存档)。
使用示例
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
mlua
还支持使用辅助包 lua-src 和 luajit-src 的 vendored lua/luajit。只需启用 vendored
功能,cargo 将自动构建和链接指定的 lua/luajit 版本。这是开始使用 mlua
的最简单方法。
独立模式
在独立模式下,mlua
允许您通过配置温和的 Lua 运行时,以确保安全性和稳定性,将脚本支持添加到您的应用程序中。
添加到 Cargo.toml
[dependencies]
mlua = { version = "0.8", features = ["lua54", "vendored"] }
main.rs
use mlua::prelude::*;
fn main() -> LuaResult<()> {
let lua = Lua::new();
let map_table = lua.create_table()?;
map_table.set(1, "one")?;
map_table.set("two", 2)?;
lua.globals().set("map_table", map_table)?;
lua.load("for k,v in pairs(map_table) do print(k,v) end").exec()?;
Ok(())
}
模块模式
在模块模式下,mlua
允许您创建一个编译后的 Lua 模块,该模块可以从 Lua 代码中使用 require
加载。在这种情况下,mlua
使用外部 Lua 运行时,这可能导致由于 Lua 环境的不确定性和使用如 debug
等库而导致的潜在不安全性。
添加到 Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.8", features = ["lua54", "vendored", "module"] }
lib.rs
:
use mlua::prelude::*;
fn hello(_: &Lua, name: String) -> LuaResult<()> {
println!("hello, {}!", name);
Ok(())
}
#[mlua::lua_module]
fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("hello", lua.create_function(hello)?)?;
Ok(exports)
}
然后(macOS 示例)
$ cargo rustc -- -C link-arg=-undefined -C link-arg=dynamic_lookup
$ ln -s ./target/debug/libmy_module.dylib ./my_module.so
$ lua5.4 -e 'require("my_module").hello("world")'
hello, world!
在 macOS 上,您需要设置额外的链接器参数。一种选择是使用 cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup
编译,另一种选择是创建一个包含以下内容的 .cargo/config
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
在 Linux 上,您可以使用 cargo build --release
正常构建模块。支持这些操作系统上的 vendored 和非 vendored 构建。
在 Windows 上,由于需要链接到 Lua dll,不支持模块的 vendored
模式。最简单的方法是使用 MinGW64(作为 MSYS2 软件包的一部分)并使用 pkg-config
,或者使用 MSVC 并设置环境变量 LUA_INC
/ LUA_LIB
/ LUA_LIB_NAME
。
有关编译和链接 Lua 模块的更多详细信息,请参阅 Building Modules 页面。
发布到 luarocks.org
为 mlua 模块提供 LuaRocks 构建后端 luarocks-build-rust-mlua
。
用 Rust 编写并发布到 luarocks 的模块
安全性
《mlua》的目标之一是提供Rust和Lua之间的安全 API。Lua C API可能引发错误longjmp的任何地方都被lua_pcall
保护,并且库的用户受到保护,不会直接与Lua栈等不安全的事物进行交互,这与安全性相关的一些开销。
遗憾的是,《mlua》即使在未使用unsafe
的情况下,也无法提供绝对的安全性。这个库包含大量的不安全代码。这个库中肯定还隐藏着一些bug!在不安全的情况下使用Lua C API是出乎意料的、令人费解的困难。
恐慌处理
mlua
将Rust回调内部生成的恐慌包装成常规的Lua错误。恐慌可以通过返回或传播Lua错误到Rust代码来恢复。
例如
let lua = Lua::new();
let f = lua.create_function(|_, ()| -> LuaResult<()> {
panic!("test panic");
})?;
lua.globals().set("rust_func", f)?;
let _ = lua.load(r#"
local status, err = pcall(rust_func)
print(err) -- prints: test panic
error(err) -- propagate panic
"#).exec();
unreachable!()
可选的mlua
可以通过pcall
/xpcall
在Lua中禁用Rust恐慌捕获,并自动在Lua API边界处恢复它们。这可以通过LuaOptions
控制,通过将Lua的pcall
/xpcall
函数包装起来,以防止捕获包装Rust恐慌的错误。
mlua
还应以另一种方式保证恐慌安全,即任何Lua
实例或句柄在用户生成的恐慌后仍然可用,并且此类恐慌不应破坏内部不变性或泄漏Lua栈空间。这对于在Drop实现中安全使用mlua
类型非常重要,因为你不应该使用恐慌来进行通用错误处理。
以下是一些mlua
行为列表,应被视为bug。如果您遇到这些问题,欢迎提交bug报告
-
如果您可以使用
mlua
造成UB而不输入“unsafe”这个词,这是一个bug。 -
如果您发现程序在包含字符串“mlua内部错误”的消息中恐慌,这是一个bug。
-
Lua C API错误通过longjmp处理。应该保护Lua C API可能在其他调用栈帧上longjmp的所有实例,除非在内部回调中这是故意的。如果您检测到
mlua
正在触发跨Rust调用栈帧的longjmp,这是一个bug! -
如果您检测到,在捕获恐慌或从恐慌引发的Drop操作期间,Lua或句柄方法触发其他bug或存在Lua栈空间泄漏,这是一个bug。
mlua
实例应在用户生成的恐慌面前保持完全可用。这一保证不适用于标记为“mlua内部错误”的恐慌,因为这已经表明了另一个bug。
沙盒
如果您对在受控环境中运行不受信任的Lua脚本感兴趣,请查看Luau沙盒页面。
mlua
提供了Lua::sandbox
方法以启用沙盒模式(仅限Luau)。
许可
本项目受MIT许可证许可。
依赖关系
~0.6–7.5MB
~48K SLoC