summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md8
-rw-r--r--README.md2
-rw-r--r--docs/release_notes/v0.9.md360
-rw-r--r--src/lib.rs2
4 files changed, 371 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18d6967..d773969 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## v0.9.0-rc.2
+
+- Added `#[derive(FromLua)]` macro to opt-in into `FromLua<T> where T: 'static + Clone` (userdata type).
+- Support vendored module mode for windows (raw-dylib linking, Rust 1.71+)
+- `module` and `vendored` features are now mutually exclusive
+- Use `C-unwind` ABI (Rust 1.71+)
+- Changed `AsChunk` trait to support capturing wrapped Lua types
+
## v0.9.0-rc.1
- `UserDataMethods::add_async_method()` takes `&T` instead of cloning `T`
diff --git a/README.md b/README.md
index 6dd4d05..028be7f 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,8 @@
> **Note**
>
> Please see the [v0.8](https://github.com/khvzak/mlua/tree/v0.8) branch for the stable versions of `mlua` released to crates.io.
+>
+> v0.9 release notes can be found [here](docs/release_notes/v0.9.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.
diff --git a/docs/release_notes/v0.9.md b/docs/release_notes/v0.9.md
new file mode 100644
index 0000000..66fbc25
--- /dev/null
+++ b/docs/release_notes/v0.9.md
@@ -0,0 +1,360 @@
+## mlua v0.9 release notes
+
+The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
+This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
+
+[CHANGELOG]: https://github.com/khvzak/mlua/blob/master/CHANGELOG.md
+
+### New features
+
+#### 1. New Any UserData API
+
+This is a long awaited feature that allows to register in Lua foreign types that cannot implement `UserData` trait because of the Rust orphan rules.
+
+Now you can register any type that implements `Any` trait as a userdata type.
+
+Consider the following example:
+
+```rust
+lua.register_userdata_type::<std::string::String>(|reg| {
+ reg.add_method("len", |_, this, ()| Ok(this.len()));
+
+ reg.add_method_mut("push", |_, this, s: String| {
+ this.push_str(&s);
+ Ok(())
+ });
+
+ reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
+})?;
+
+let s = lua.create_any_userdata("hello".to_string())?;
+lua.load(chunk! {
+ print("s:len() is " .. $s:len())
+ $s:push(" world")
+ // Prints: hello, world
+ print($s)
+})
+.exec()?;
+```
+
+In this example we registered [`std::string::String`] as a userdata type with a set of methods and then created an instance of this type in Lua.
+
+It's _not_ required to register a type before using the `Lua::create_any_userdata()` method, instead an empty metatable will be created for you.
+You can also register the same type multiple times with different methods. Any previously created instances will share the old metatable, while new instances will have the new one.
+
+The new set of API is called `any_userdata` because it allows to register types that implements `Any` trait.
+
+[`std::string::String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
+
+#### 2. Scope support for the new any userdata types
+
+When you need to create non-static userdata instances in Lua, the usual way is use `Lua::scope()` helper to make them scoped. When out of scope, any scoped objects will be automatically
+dropped. The only downside of this approach is that every new instance will have a new metatable. This is not very fast if you need to create a lot of instances.
+
+With the new Any UserData API, you can place non-static references `&T` where `T: 'static` into a scope and they will share a single static metatable.
+
+```rust
+lua.register_userdata_type::<std::string::String>(|reg| {
+ reg.add_method_mut("replace", |_, this, (pat, to): (String, String)| {
+ *this = this.replace(&pat, &to);
+ Ok(())
+ });
+
+ reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
+})?;
+
+let mut s = "hello, world".to_string();
+
+lua.scope(|scope| {
+ // This userdata instance holds only a mutable reference to our string
+ let ud = scope.create_any_userdata_ref_mut(&mut s)?;
+ lua.load(chunk! {
+ $ud:replace("world", "user!")
+ })
+ .exec()
+})?;
+
+// Prints: hello, user!
+println!("{s}!");
+```
+
+#### 3. Owned types (_unstable_)
+
+One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
+
+In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData` that are `'static` (no lifetime attached).
+
+```rust
+let lua = Lua::new();
+
+struct MyStruct {
+ table: OwnedTable,
+ func: OwnedFunction,
+}
+
+let my_struct = MyStruct {
+ table: lua.globals().into_owned(),
+ func: lua
+ .create_function(|_, t: Table| Ok(format!("{t:#?}")))?
+ .into_owned(),
+};
+
+// It's safe to drop Lua!
+drop(lua);
+
+let result = my_struct.func.call::<_, String>(my_struct.table)?;
+println!("{result}");
+```
+
+Prior to v0.9, it was possible to do by creating a reference to the Lua value in registry using `Lua::create_registry_value()`
+and retrieving value later using `Lua::registry_value()` method.
+
+All owned handles hold a *strong* reference to the current Lua instance.
+Be warned, if you place them into a Lua type (eg. `UserData` or a Rust callback), it is *very easy*
+to accidentally cause reference cycles that would prevent destroying Lua instance.
+
+Please note this functionality is available under the `unstable` feature flag and not available when the `send` feature is enabled.
+
+#### New ffi module
+
+In v0.9 release the internal `ffi` module has been moved into the new [`mlua-sys`] crate and became available for public use.
+This crate provides unified Lua FFI API (targeting Lua 5.4) using a (limited) compatibility layer for older versions.
+
+mlua re-exports the `ffi` module aliasing the `mlua-sys` crate and provides (unsafe) functionality to work with raw Lua state:
+
+```rust
+unsafe {
+ unsafe extern "C-unwind" fn lua_add(state: *mut mlua::lua_State) -> i32 {
+ let a = mlua::ffi::luaL_checkinteger(state, 1);
+ let b = mlua::ffi::luaL_checkinteger(state, 2);
+ mlua::ffi::lua_pushinteger(state, a + b);
+ 1
+ }
+
+ let add = lua.create_c_function(lua_add)?;
+ assert_eq!(add.call::<_, i32>((2, 3))?, 5);
+}
+```
+
+[`mlua-sys`]: https://crates.io/crates/mlua-sys
+
+#### Luau JIT support
+
+mlua brings support for the new experimental [Luau] JIT backend under the `luau-jit` feature flag. This backend is still under development and not yet ready for production use.
+
+To enable it, just call `lua.enable_jit(true)` before loading Lua code. mlua will automatically trigger JIT compilation for new Lua chunks.
+
+When calling this function with `false` argument, mlua will disable JIT compilation but any previously compiled chunks will remain JIT-compiled.
+
+[Luau]: https://luau-lang.org
+
+### Improvements
+
+#### 1. Better error reporting
+
+When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported a error message without any context or reference to the particular argument.
+
+In v0.9 it reports a error message with the argument index and expected type:
+
+```rust
+let func = lua.create_function(|_, _a: i32| Ok(()))?;
+lua.load(chunk! {
+ local ok, err = pcall($func, "not a number")
+ // Prints: bad argument #1: error converting Lua string to i32 (expected number or string coercible to number)
+ print(err)
+})
+.exec()?;
+```
+
+Similar changes have been made for userdata functions and methods:
+
+```rust
+lua.register_userdata_type::<&'static str>(|reg| {
+ reg.add_method("len", |_, this, ()| Ok(this.len()));
+})?;
+
+let s = lua.create_any_userdata("hello")?;
+lua.load(chunk! {
+ local ok, err = pcall($s.len, 123)
+ // Prints: bad argument `self` to `&str.len`: error converting Lua integer to userdata
+ print(err)
+})
+.exec()?;
+```
+
+#### 2. Error context
+
+Similar to the [`anyhow`] Error type, now it's possible to attach context to Lua errors:
+
+```rust
+let read = lua.create_function(|lua, path: String| {
+ let bytes = std::fs::read(&path)
+ .into_lua_err()
+ .context(format!("Failed to open `{path}`"))?;
+ Ok(lua.create_string(bytes))
+})?;
+
+lua.load(chunk! {
+ local ok, err = pcall($read, "/nonexistent")
+ print(err)
+})
+.exec()?;
+
+Prints:
+```text
+Failed to open /nonexistent
+No such file or directory (os error 2)
+stack traceback:
+...
+```
+
+[`anyhow`]: https://crates.io/crates/anyhow
+
+#### 4. New methods `Function::wrap`/`AnyUserData::wrap`
+
+Sometimes it's useful to have `IntoLua` trait implementation for a Rust function or type `T: Any` without needing to call `Lua::create_function()`/`Lua::create_any_userdata()` methods.
+Since v0.9 you can call the new methods `Function::wrap()`/`AnyUserData::wrap()` that allows to do this. They return an abstract type that `impl IntoLua`:
+
+```rust
+lua.globals().set("print_rust", Function::wrap(|_, s: String| Ok(println!("{}", s))))?;
+lua.globals().set("rust_ud", AnyUserData::wrap("hello"))?;
+```
+
+In addition there are also `Function::wrap_mut()`/`Function::wrap_async()` methods that allow to wrap mutable and async functions respectively.
+
+For a `T: 'UserData + 'static` the `IntoLua` trait is still always implemented.
+
+#### `UserDataRef` and `UserDataRefMut` type wrappers
+
+The new wrappers `UserDataRef` and `UserDataRefMut` are receivers for userdata type `T` and borrow underlying instance for the lifetime of the wrapper.
+
+```rust
+lua.globals()
+ .set("ud", AnyUserData::wrap("hello".to_string()))?;
+
+let mut ud_mut: UserDataRefMut<String> = lua.globals().get("ud")?;
+ud_mut.push_str(", Rust");
+drop(ud_mut);
+
+let ud_ref: UserDataRef<String> = lua.globals().get("ud")?;
+// Prints: hello, Rust
+println!("{}", *ud_ref);
+```
+
+In the previous mlua versions the same functionality can be achieved by receiving `AnyUserData` and calling `AnyUserData::borrow()`/`AnyUserData::borrow_mut()` methods.
+
+#### New `AnyUserDataExt` trait
+
+Similar to the `TableExt` trait, the `AnyUserDataExt` provides a set of extra methods for the `AnyUserData` type.
+
+1) `AnyUserDataExt::get()/set()` to get/set a value by key from the userdata, assuming it has `__index` metamethod.
+
+2) `AnyUserDataExt::call()` to call the userdata as a function assuming it has `__call` metamethod.
+
+3) `AnyUserData::call_method(name, ...)` to call the userdata method, assuming it has `__index` metamethod and the associated function.
+
+#### Pretty formatting Lua values
+
+`mlua::Value` implements a new format `:#?` that allows to pretty print Lua values:
+
+```rust
+println!("{:#?}", lua.globals());
+```
+
+Prints:
+```
+{
+ ["_G"] = table: 0x7fa2d0706260,
+ ["_VERSION"] = "Lua 5.4",
+ ["assert"] = function: 0x10451d11d,
+ ["collectgarbage"] = function: 0x10451d198,
+ ["coroutine"] = {
+ ["close"] = function: 0x10451e28f,
+ ...
+ },
+ ["dofile"] = function: 0x10451d37c,
+ ...
+}
+```
+
+In addition a new method `Value::to_string()` was added to convert `Value` to a string (using `__tostring` metamethod if available).
+
+#### Environment for Lua functions
+
+Any Lua functions have an associated environment table that is used to resolve global variables. By default it sets to a Lua globals table.
+
+In the new release it's possible to get or update a function environment using `Function::environment()` or `Function::set_environment()` methods respectively.
+
+```rust
+let f = lua.load("return a").into_function()?;
+
+assert_eq!(f.environment(), Some(lua.globals()));
+
+lua.globals().set("a", 1)?;
+assert_eq!(f.call::<_, i32>(())?, 1);
+
+f.set_environment(lua.create_table_from([("a", "hello")])?)?;
+assert_eq!(f.call::<_, mlua::String>(())?, "hello");
+```
+
+#### Performance optimizations
+
+The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
+
+[benchmarks results]: https://github.com/khvzak/script-bench-rs
+
+### Changes in `module` mode
+
+#### New attributes
+
+The `lua_module` macro now support the following attributes:
+
+- `name=...` - sets name of the module (defaults to the name of the function).
+
+Eg.:
+
+```rust
+#[mlua::lua_module(name = "alt_module")]
+fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
+ lua.create_table()
+}
+```
+
+Under the hood a new function `luaopen_alt_module` will be created for the Lua module loader.
+
+- `skip_memory_check` - skip memory allocation checks for some operations.
+
+In module mode, mlua runs in unknown environment and cannot say are there any memory limits or not. As result, some operations that require memory allocation runs in
+protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
+
+#### Improved Windows target
+
+In previous mlua versions, building a Lua module for Windows requires having Lua development libraries installed on the system.
+In contrast, on Linux and macOS, modules can be built without any external dependencies, using `-undefined=dynamic_lookup` linker flag.
+
+With Rust 1.71+ it's now possible to lift this restriction for Windows as well. You can build modules normally and they will be linked with
+`lua54.dll`/`lua53.dll`/`lua52.dll`/`lua51.dll` depending on the enabled Lua version.
+
+You still need to have the dll although, linked to application where the module will be loaded.
+
+### Breaking changes
+
+1) `ToLua`/`ToLuaMulti` traits have been renamed to `IntoLua`/`IntoLuaMulti` respectively (with the methods called `into_lua`/`into_lua_multi`).
+
+The main reason for this change is following the Rust self [convention](https://rust-lang.github.io/rust-clippy/master/index.html#/wrong_self_convention).
+
+2) Removed `FromLua` implementation for `T: UserData + Clone`.
+
+During the usage of mlua, it was found that this implementation is not very useful and prevents custom `FromLua` implementations for `T: UserData`.
+It should be a developer decision to opt-in `FromLua` for their `T` if needed rather than having enabled it unconditionally.
+
+To opt-in `FromLua` for `T: Clone` you can use a simple `#[derive(FromLua)]` macro (requires `feature = "macros"`):
+
+```rust
+use mlua::FromLua;
+
+#[derive(Clone, Copy, FromLua)]
+struct MyUserData(i32);
+```
+
+`T` is not required to implement `UserData` because of the new relaxed restrictions on userdata types.
diff --git a/src/lib.rs b/src/lib.rs
index 4f6ea09..f157a41 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -256,7 +256,7 @@ pub use mlua_derive::FromLua;
///
/// In module mode, mlua runs in unknown environment and cannot say are there any memory
/// limits or not. As result, some operations that require memory allocation runs in
-/// protected mode. Setting this mode will improve performance of such operations
+/// protected mode. Setting this attribute will improve performance of such operations
/// with risk of having uncaught exceptions and memory leaks.
///
/// ```ignore