mlua icon indicating copy to clipboard operation
mlua copied to clipboard

FR: Add a `is_destructed()` to `AnyUserData`

Open sxyazi opened this issue 10 months ago • 2 comments

I want to implement copy-on-write for a add_field_function_get(): I have a File that remains immutable throughout Lua::scope(), and thus its url property is also immutable, and since that url property is frequently used, I cache it as follows:

fields.add_field_function_get("url", |lua, ud| {
  ud.borrow_mut_scoped(|me: &mut Self| {
    Ok(match &me.cache {
      Some(v) => v.clone(),  // Reuse the cached `AnyUserData`
      None => {              // Create a new `AnyUserData` and cache it
        me.cache = Some(lua.create_any_userdata(me.url.clone())?);
        me.cache.clone().unwrap()
      }
    })
  })?
});

For some Lua APIs that merely accept a borrowed UserData value (UserDataRef<Url>), this works well:

fs.metadata(file.url)

However, there are some APIs that accept an owned UserData, so they take ownership of the Url:

fs.File { url = file.url, ... }

fs.File will call AnyUserData::take() to take ownership of the Url, which leads to me.cache being destructed. So, I would like a way to check whether the UserData is still available:

-   Ok(match &me.cache {
+   Ok(match me.cache.as_ref().filter(|ud| !ud.is_destructed()) {

Perhaps is_destroyed() is a better name than is_destructed() in order to be consistent with the existing UserData::destroy().

sxyazi avatar Apr 10 '25 06:04 sxyazi

A quickest solution would be:

let ud = lua.create_any_userdata("abc")?;
ud.destroy()?;

// Check that userdata is destructed
assert!(matches!(ud.borrow::<()>(), Err(Error::UserDataDestructed)));

khvzak avatar Apr 10 '25 23:04 khvzak

It works great, thank you!

sxyazi avatar Apr 16 '25 12:04 sxyazi