| 
 | 1 | +## mlua v0.10 release notes  | 
 | 2 | + | 
 | 3 | +The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.  | 
 | 4 | +This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].  | 
 | 5 | + | 
 | 6 | +[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md  | 
 | 7 | + | 
 | 8 | +### New features  | 
 | 9 | + | 
 | 10 | +#### `'static` Lua types  | 
 | 11 | + | 
 | 12 | +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.  | 
 | 13 | +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.  | 
 | 14 | + | 
 | 15 | +#### Truly `send` feature  | 
 | 16 | + | 
 | 17 | +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.  | 
 | 18 | + | 
 | 19 | +```rust  | 
 | 20 | +let lua = Lua::new();  | 
 | 21 | + | 
 | 22 | +lua.globals().set("i", 0)?;  | 
 | 23 | +let func = lua.load("i = i + ...").into_function()?;  | 
 | 24 | + | 
 | 25 | +std::thread::scope(|s| {  | 
 | 26 | +    s.spawn(|| {  | 
 | 27 | +        for i in 0..5 {  | 
 | 28 | +            func.call::<()>(i).unwrap();  | 
 | 29 | +        }  | 
 | 30 | +    });  | 
 | 31 | +    s.spawn(|| {  | 
 | 32 | +        for i in 0..5 {  | 
 | 33 | +            func.call::<()>(i).unwrap();  | 
 | 34 | +        }  | 
 | 35 | +    });  | 
 | 36 | +});  | 
 | 37 | + | 
 | 38 | +assert_eq!(lua.globals().get::<i32>("i")?, 20);  | 
 | 39 | +```  | 
 | 40 | + | 
 | 41 | +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.  | 
 | 42 | + | 
 | 43 | +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.  | 
 | 44 | + | 
 | 45 | +[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html  | 
 | 46 | + | 
 | 47 | +#### Register Rust functions with variable number of arguments  | 
 | 48 | + | 
 | 49 | +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.  | 
 | 50 | + | 
 | 51 | +They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:  | 
 | 52 | + | 
 | 53 | +```rust  | 
 | 54 | +let add = Function::wrap(|a: i64, b: i64| Ok(a + b));  | 
 | 55 | + | 
 | 56 | +lua.globals().set("add", add).unwrap();  | 
 | 57 | + | 
 | 58 | +// Prints 50  | 
 | 59 | +lua.load(r#"print(add(5, 45))"#).exec().unwrap();  | 
 | 60 | +```  | 
 | 61 | + | 
 | 62 | +To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.  | 
 | 63 | + | 
 | 64 | +#### Setting metatable for Lua builtin types  | 
 | 65 | + | 
 | 66 | +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.  | 
 | 67 | + | 
 | 68 | +```rust  | 
 | 69 | +let mt = lua.create_table()?;  | 
 | 70 | +mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;  | 
 | 71 | +lua.set_type_metatable::<bool>(Some(mt));  | 
 | 72 | +lua.load("assert(tostring(true) == '2')").exec().unwrap();  | 
 | 73 | +```  | 
 | 74 | + | 
 | 75 | +### Improvements  | 
 | 76 | + | 
 | 77 | +#### New `ObjectLike` trait  | 
 | 78 | + | 
 | 79 | +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.  | 
 | 80 | + | 
 | 81 | +#### `Either<L, R>` enum  | 
 | 82 | + | 
 | 83 | +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.  | 
 | 84 | +This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.  | 
 | 85 | + | 
 | 86 | +```rust  | 
 | 87 | +let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));  | 
 | 88 | + | 
 | 89 | +lua.globals().set("func", func).unwrap();  | 
 | 90 | + | 
 | 91 | +// Prints: received: 123  | 
 | 92 | +lua.load(r#"print(func(123))"#).exec().unwrap();  | 
 | 93 | + | 
 | 94 | +// Prints: bad argument #1: error converting Lua table to Either<i32, String>  | 
 | 95 | +lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();  | 
 | 96 | +```  | 
 | 97 | + | 
 | 98 | +#### `Lua::exec_raw` helper to execute low-level Lua C API code  | 
 | 99 | + | 
 | 100 | +For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.  | 
 | 101 | + | 
 | 102 | +```rust  | 
 | 103 | +let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;  | 
 | 104 | +let sum: i64 = unsafe {  | 
 | 105 | +    lua.exec_raw(&t, |state| {  | 
 | 106 | +        // top of the stack: table `t`  | 
 | 107 | +        let mut sum = 0;  | 
 | 108 | +        // push nil as the first key  | 
 | 109 | +        mlua::ffi::lua_pushnil(state);  | 
 | 110 | +        while mlua::ffi::lua_next(state, -2) != 0 {  | 
 | 111 | +            sum += mlua::ffi::lua_tointeger(state, -1);  | 
 | 112 | +            // Remove the value, keep the key for the next iteration  | 
 | 113 | +            mlua::ffi::lua_pop(state, 1);  | 
 | 114 | +        }  | 
 | 115 | +        mlua::ffi::lua_pop(state, 1);  | 
 | 116 | +        mlua::ffi::lua_pushinteger(state, sum);  | 
 | 117 | +        // top of the stack: sum  | 
 | 118 | +    })  | 
 | 119 | +}?;  | 
 | 120 | +assert_eq!(sum, 15);  | 
 | 121 | +```  | 
 | 122 | + | 
 | 123 | +The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.  | 
0 commit comments