#lua #lua-bindings #luau #lua-jit #scripting #async #scripting-language

mlua

为Lua 5.4/5.3/5.2/5.1(包括LuaJIT)和Roblox Luau提供高级绑定,具有异步/await特性,并支持在Rust中编写原生Lua模块

64个版本

0.10.0-beta.12024年7月31日
0.9.8 2024年5月15日
0.9.6 2024年2月29日
0.9.2 2023年11月22日
0.2.0 2019年11月30日

异步类别中排名3

Download history 11494/week @ 2024-05-04 11406/week @ 2024-05-11 11148/week @ 2024-05-18 10523/week @ 2024-05-25 11720/week @ 2024-06-01 12059/week @ 2024-06-08 11907/week @ 2024-06-15 11795/week @ 2024-06-22 9651/week @ 2024-06-29 10926/week @ 2024-07-06 11816/week @ 2024-07-13 12758/week @ 2024-07-20 11766/week @ 2024-07-27 11272/week @ 2024-08-03 14607/week @ 2024-08-10 11926/week @ 2024-08-17

每月下载量51,788
用于139个crates(其中121个直接使用)

MIT许可协议

1MB
17K SLoC

mlua

Build Status Latest Version API Documentation Coverage Status MSRV

导览 | 基准测试 | 常见问题解答

主分支是mlua的v0.10开发版本。请查看v0.9分支以获取mlua的稳定版本。

mlua是为Rust编写的Lua编程语言的绑定,旨在提供安全(尽可能做到),高级、易于使用、实用且灵活的API。

作为rlua的分支,mlua支持Lua 5.4、5.3、5.2、5.1(包括LuaJIT)以及Roblox Luau,并允许在Rust中编写原生Lua模块,以及以独立模式使用Lua。

mlua已在Windows/macOS/Linux上进行测试,包括在GitHub Actions上的x86_64平台上的模块模式以及交叉编译到aarch64(也支持其他目标)。

通过wasm32-unknown-emscripten目标支持WebAssembly(WASM),适用于所有Lua版本(不包括JIT)。

使用方法

功能标志

mlua使用功能标志来减少依赖项数量、编译代码,并允许选择所需的功能集。以下是可用功能标志的列表。默认情况下,mlua不启用任何功能。

  • lua54:激活Lua 5.4 支持
  • lua53:激活 Lua 5.3 支持
  • lua52:激活 Lua 5.2 支持
  • lua51:激活 Lua 5.1 支持
  • luajit:激活 LuaJIT 支持
  • luajit52:激活与 Lua 5.2 部分兼容的 LuaJIT 支持
  • luau:激活 Luau 支持(自动 vendored 模式)
  • luau-jit:激活带有 JIT 后端的 Luau 支持
  • luau-vector4:激活带有 4 维向量的 Luau 支持
  • vendored:在 mlua 编译期间使用 lua-srcluajit-src crates 从源码构建静态 Lua (JIT) 库
  • module:启用模块模式(为 Lua 构建可加载的 cdylib 库)
  • async:启用 async/await 支持(可以使用任何执行器,例如 tokioasync-std
  • send:使 mlua::Lua 可在线程边界之间传输(向 mlua::Functionmlua::UserData 添加 Send 要求)
  • serialize:使用 serde 框架为 mlua 类型添加序列化和反序列化支持
  • macros:启用过程宏(例如 chunk!
  • parking_lot:支持包装在 parking_lot 的原语中的 UserData 类型(Arc<Mutex>Arc<RwLock>
  • unstable:启用 不稳定 功能。这些功能的公共 API 可能会在发布之间中断

Async/await 支持

mlua 支持所有 Lua 版本的 async/await,包括 Luau。

这使用 Lua 协程 实现,并需要在 Thread 中运行,同时在 Cargo.toml 中启用 feature = "async"

示例:

shell 命令示例:

# async http client (hyper)
cargo run --example async_http_client --features=lua54,async,macros

# async http client (reqwest)
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize

# async http server
cargo run --example async_http_server --features=lua54,async,macros
curl -v https://127.0.0.1:3000

序列化(serde)支持

启用 serialize 功能标志后,mlua 允许您将实现 serde::Serializeserde::Deserialize 的任何类型序列化和反序列化到/从 mlua::Value。此外,mlua 为其提供了 serde::Serialize 特性实现(包括对 UserData 的支持)。

示例

编译

您必须根据所选的 Lua 版本启用以下功能之一:lua54lua53lua52lua51luajit(52)luau

默认情况下,mlua 使用 pkg-config 工具查找所选 Lua 版本的 lua 包含文件和库。在大多数情况下,它按预期工作,尽管有时可能更希望使用自定义 lua 库。为了实现这一点,mlua 支持 LUA_LIBLUA_LIB_NAMELUA_LINK 环境变量。 LUA_LINK 是可选的,可以是 dylib(动态库)或 static(静态库,.a 归档)。

如何使用它们的示例

my_project $ 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.9.9", 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 代码使用 require 加载的编译 Lua 模块。在这种情况下,mlua 使用外部 Lua 运行时,这可能导致由于 Lua 环境的不确定性和使用如 debug 等库而导致的潜在不安全性。

示例

添加到 Cargo.toml

[lib]
crate-type = ["cdylib"]

[dependencies]
mlua = { version = "0.9.9", features = ["lua54", "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 正常构建模块。

在Windows上,目标模块将与lua5x.dll库链接(取决于您的功能标志)。您的应用程序应该提供这个库。

模块构建不需要在系统上安装Lua库或头文件。

发布到luarocks.org

为mlua模块提供了一个LuaRocks构建后端luarocks-build-rust-mlua

用Rust编写的模块并发布到luarocks

安全性

mlua的一个目标是为Rust和Lua之间提供安全的 API。Lua C API可能会在任何地方触发错误longjmp的地方,都由lua_pcall进行保护,并且库的使用者被保护免于直接与不安全的事物(如Lua栈)交互,这与安全性相关联。

不幸的是,即使在没有使用unsafe的情况下,mlua也不提供绝对的安全性。这个库包含大量的不安全代码。这个库中肯定还潜伏着许多错误!在不安全性的潜在风险下使用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行为列表。如果您遇到它们,非常欢迎您提交错误报告

  • 如果您可以不用键入“unsafe”这个词,使用mlua导致UB,这是一个错误。

  • 如果您的程序恐慌并显示包含字符串“mlua内部错误”的消息,这是一个错误。

  • 由longjmp处理的Lua C API错误。除内部回调中故意这样做外,所有Lua C API可能会在调用堆栈帧上触发longjmp的实例都应该受到保护。如果您检测到mlua正在触发跨越您的Rust堆栈帧的longjmp,这是一个错误!

  • 如果您检测到在捕获恐慌或从恐慌触发的Drop操作期间,Lua或处理方法触发了其他错误或存在Lua栈空间泄露,这是一个错误。mlua实例在用户生成的恐慌面前应保持完全可用。这种保证不适用于标记为“mlua内部错误”的恐慌,因为这已经表明了另一个错误。

沙盒

如果您对在受控环境中运行不受信任的Lua脚本感兴趣,请查看Luau沙盒页面。

mlua提供了一个用于启用沙盒模式的Lua::sandbox方法(仅限于Luau)。

许可证

本项目采用MIT许可协议

依赖项

~1–8.5MB
~59K SLoC