diff --git a/README.md b/README.md index 0992163..3a0efe8 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ # The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`. +> **Note** +> +> See (upcoming) v0.10 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.10.md). + `mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide _safe_ (as far as it's possible), high level, easy to use, practical and flexible API. @@ -53,6 +57,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl * `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`) * `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework * `macros`: enable procedural macros (such as `chunk!`) +* `anyhow`: enable `anyhow::Error` conversion into Lua [5.4]: https://www.lua.org/manual/5.4/manual.html [5.3]: https://www.lua.org/manual/5.3/manual.html @@ -128,7 +133,7 @@ Add to `Cargo.toml` : ``` toml [dependencies] -mlua = { version = "0.9.9", features = ["lua54", "vendored"] } +mlua = { version = "0.10.0-rc.1", features = ["lua54", "vendored"] } ``` `main.rs` @@ -163,7 +168,7 @@ Add to `Cargo.toml` : crate-type = ["cdylib"] [dependencies] -mlua = { version = "0.9.9", features = ["lua54", "module"] } +mlua = { version = "0.10.0-rc.1", features = ["lua54", "module"] } ``` `lib.rs` : diff --git a/docs/release_notes/v0.10.md b/docs/release_notes/v0.10.md new file mode 100644 index 0000000..466929d --- /dev/null +++ b/docs/release_notes/v0.10.md @@ -0,0 +1,123 @@ +## 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::("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::(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` enum + +The `Either` 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| 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 +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.