## 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/mlua-rs/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.

#### `anyhow` feature flag

The new `anyhow` feature flag adds `IntoLua` and `Into<mlua::Error>` implementation for the `anyhow::Error` type.

```rust
let f = lua.create_function(|_, ()| {
    Err(anyhow!("error message"))?;
    Ok(())
})?;
```

### Breaking changes

#### Scope changes

The following `Scope` methods were changed:
- Removed `Scope::create_any_userdata`
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`

Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.

`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
In mlua v0.9 this can cause read-after-free bug in some edge cases.

To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:

```rust
let data = "hello".to_string();
lua.scope(|scope| {
    let ud = scope.create_any_userdata_ref(&data)?;

    // We can only borrow scoped userdata using this method
    ud.borrow_scoped::<String, ()>(|s| {
        assert_eq!(s, "hello");
    })?;

    Ok(())
})?;
```

Those methods work for scoped and regular userdata objects (but still require `T: 'static`).

#### String changes

Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
Lua instance can be destroyed while reference to the data is still alive:

```rust
let lua = Lua::new();
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
let s_ref: &str = s.to_str()?; // this is not safe!
drop(lua);
println!("{s_ref}"); // use after free!
```

To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.

These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:

```rust
let lua = Lua::new();
let s: mlua::String = lua.create_string("hello, world")?;
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
drop(lua);
println!("{s_ref}"); // ok
```

The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
The same applies to `BorrowedBytes`.

Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
