#lua #lua-bindings #factorio #luau #scripting #lua-jit #high-level

factorio-mlua

为Lua 5.4/5.3/5.2/5.1(包括LuaJIT)和Roblox Luau提供高级绑定,具有异步/await功能,并支持在Rust中编写本地Lua模块。增加了Factorio Lua支持的分支

7个版本

0.9.0-beta.4 2024年8月22日
0.9.0-beta.22023年5月20日
0.8.0 2022年6月9日
0.8.0-beta.5-32022年5月28日
0.8.0-beta.5-22022年5月25日

#101 in 异步

每月 27 次下载

MIT 许可证

755KB
16K SLoC

mlua

Build Status Latest Version API Documentation Coverage Status MSRV

指南 | 基准测试 | 常见问题解答

注意

请参阅 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-srcluajit-src crates
  • module:启用模块模式(为 Lua 构建可加载的 cdylib 库)
  • async:启用 async/await 支持(可以使用任何执行器,例如 tokioasync-std
  • send:使 mlua::Lua 能够跨线程边界传输(向 mlua::Functionmlua::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::Serializeserde::Deserialize 的任何类型序列化和反序列化为 mlua::Value。此外,mlua 还为其提供 serde::Serialize 特性实现(包括对 UserData 的支持)。

示例

编译

您必须启用以下功能之一: lua54lua53lua52lua51luajit(52)luau,具体取决于所选择的 Lua 版本。

默认情况下,mlua 使用 pkg-config 工具查找所选 Lua 版本的 lua 包含文件和库。在大多数情况下,它按预期工作,尽管有时可能更倾向于使用自定义 lua 库。为了实现这一点,mlua 支持以下环境变量:LUA_INCLUA_LIBLUA_LIB_NAMELUA_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-srcluajit-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