(mlua) Borrowed data escapes out of closure

I want to embed a Lua interpreter (with crate mlua) and expose to that interpreter a function which returns userdata.

Here is a minimal example, where I replaced mlua's API by trivial functions with similar type signatures (a few parameters and Results have been removed):

#![allow(unused_variables,dead_code)]
#![warn(elided_lifetimes_in_paths)]
use std::marker::{PhantomData};
use std::cell::{Cell};

// Miniature -version of mlua interface
struct Lua();
struct Scope<'lua,'scope>(&'lua Lua, PhantomData<Cell<&'scope ()>>);
struct Function<'lua>(&'lua Lua);
struct AnyUserData<'lua>(&'lua Lua);

impl Lua {
  pub fn scope<'lua, 'scope, F, R>(&'lua self, f: F)->R
  where F: FnOnce(&Scope<'lua, 'scope>)->R, 'lua: 'scope {
    f(&Scope(self, PhantomData::<Cell::<&'scope ()>>))
  }
  pub fn new()->Self { Self() }
}

impl<'lua, 'scope> Scope<'lua, 'scope> {
  pub fn create_function<'callback, F, R>(&'callback self, func: F)->Function<'lua>
  where F: 'scope + Fn()->R {
    Function(self.0)
  }
  pub fn create_nonstatic_userdata<T: 'scope>(&self, data: T) -> AnyUserData<'lua> {
    AnyUserData(self.0)
  }
}

fn main() {
  let lua = Lua::new();
  lua.scope(|scope| {
    let f = || { scope.create_nonstatic_userdata(()) };
    scope.create_function(f);
  });
}

(Playground)

(compared to the previous example, I made this one quite closer to reality, and indeed the error is not the same).

The function f should then be made visible to the Lua interpreter.
This code fails to compile with error E0521: borrowed data escapes out of closure; this is caused by the create_function() call (this error disappears when I remove either of the create_* calls).

However, I don't see where scope escapes the closure body: obviously, nothing happens outside of the closure body, and the create_function return value is dropped at the end of the closure (even before, in this example, since the return value of the call is not even used). Moreover, replacing this function call by its definition (Function(scope.0) unless I'm mistaken) also makes the code compile.

What is happening here? Surely, exporting functions which build userdata is not a marginal use-case for embedding Lua in a project? How should I do this?

If you follow the compiler's suggestion of adding move, then it compiles. Probably, there is an unintended reborrow, because closures only move/copy data if they need to; by default, they borrow all captured variables instead.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.