mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
124 lines
4.7 KiB
Markdown
124 lines
4.7 KiB
Markdown
## mlua v0.10 release notes
|
|
|
|
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
|
|
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
|
|
|
|
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
|
|
|
|
### New features
|
|
|
|
#### `'static` Lua types
|
|
|
|
In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
|
|
In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
|
|
|
|
#### Truly `send` feature
|
|
|
|
In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
|
|
|
|
```rust
|
|
let lua = Lua::new();
|
|
|
|
lua.globals().set("i", 0)?;
|
|
let func = lua.load("i = i + ...").into_function()?;
|
|
|
|
std::thread::scope(|s| {
|
|
s.spawn(|| {
|
|
for i in 0..5 {
|
|
func.call::<()>(i).unwrap();
|
|
}
|
|
});
|
|
s.spawn(|| {
|
|
for i in 0..5 {
|
|
func.call::<()>(i).unwrap();
|
|
}
|
|
});
|
|
});
|
|
|
|
assert_eq!(lua.globals().get::<i32>("i")?, 20);
|
|
```
|
|
|
|
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
|
|
|
|
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
|
|
|
|
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
|
|
|
|
#### Register Rust functions with variable number of arguments
|
|
|
|
The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
|
|
|
|
They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
|
|
|
|
```rust
|
|
let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
|
|
|
|
lua.globals().set("add", add).unwrap();
|
|
|
|
// Prints 50
|
|
lua.load(r#"print(add(5, 45))"#).exec().unwrap();
|
|
```
|
|
|
|
To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
|
|
|
|
#### Setting metatable for Lua builtin types
|
|
|
|
For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
|
|
|
|
```rust
|
|
let mt = lua.create_table()?;
|
|
mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
|
|
lua.set_type_metatable::<bool>(Some(mt));
|
|
lua.load("assert(tostring(true) == '2')").exec().unwrap();
|
|
```
|
|
|
|
### Improvements
|
|
|
|
#### New `ObjectLike` trait
|
|
|
|
The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
|
|
|
|
#### `Either<L, R>` enum
|
|
|
|
The `Either<L, R>` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
|
|
This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
|
|
|
|
```rust
|
|
let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));
|
|
|
|
lua.globals().set("func", func).unwrap();
|
|
|
|
// Prints: received: 123
|
|
lua.load(r#"print(func(123))"#).exec().unwrap();
|
|
|
|
// Prints: bad argument #1: error converting Lua table to Either<i32, String>
|
|
lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
|
|
```
|
|
|
|
#### `Lua::exec_raw` helper to execute low-level Lua C API code
|
|
|
|
For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
|
|
|
|
```rust
|
|
let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
|
|
let sum: i64 = unsafe {
|
|
lua.exec_raw(&t, |state| {
|
|
// top of the stack: table `t`
|
|
let mut sum = 0;
|
|
// push nil as the first key
|
|
mlua::ffi::lua_pushnil(state);
|
|
while mlua::ffi::lua_next(state, -2) != 0 {
|
|
sum += mlua::ffi::lua_tointeger(state, -1);
|
|
// Remove the value, keep the key for the next iteration
|
|
mlua::ffi::lua_pop(state, 1);
|
|
}
|
|
mlua::ffi::lua_pop(state, 1);
|
|
mlua::ffi::lua_pushinteger(state, sum);
|
|
// top of the stack: sum
|
|
})
|
|
}?;
|
|
assert_eq!(sum, 15);
|
|
```
|
|
|
|
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
|