Cannot move out a captured variable in an `FnMut` closure

Look at this code:

use mlua::{Lua, Table};
fn init(xxx: SomeTypeWithoutCopy, lua: &Lua) {
	lua.globals().set(
		"base",
		lua.create_function_mut(move |luav, p: (String, u32, Table)| base(luav, p, xxx)).unwrap(),
	);
}
fn base(
	_: &Lua,
	(fn_name, args, argv): (String, u32, Table),
	xxx: SomeTypeWithoutCopy,
) -> Result<(), mlua::Error> {
	//Some other code
}

The compiler tells me that:

error[E0507]: cannot move out of `xxx`, a captured variable in an `FnMut` closure
lua.create_function_mut(move |luav, p: (String, u32, Table)| base(luav, p, xxx)).unwrap(),
                        ------------------------------------               ^^^ move occurs because `xxx` has type `Game`, which does not implement the `Copy` trait
                        |
                        captured by this `FnMut` closure

It works well if I change the code to that:

// ...
lua.create_function_mut(move |luav, p: (String, u32, Table)| base(luav, p, xxx.clone())).unwrap()
// ...

But Must clone be here? I think the variable should can move into the closure.

Edit:
Okay. Thank to alice and kpreid.
The function "base" will change some value in xxx. And I hope if call the closure, "base" can read those changed values.
I think that code can solve problem:

use mlua::{Lua, Table};
fn init(mut xxx: SomeTypeWithoutCopy, lua: &Lua) {
	lua.globals().set(
		"base",
		lua.create_function_mut(move |luav, p: (String, u32, Table)| base(luav, p, &mut xxx)).unwrap(),
	);
}
fn base(
	_: &Lua,
	(fn_name, args, argv): (String, u32, Table),
	xxx: &mut SomeTypeWithoutCopy,
) -> Result<(), mlua::Error> {
	//Some other code
}

But just for curious, What variable do move into the closure? The "xxx" or "&mut xxx"?

The value is moved into the closure. The problem is that an FnMut closure can be called multiple times, so the closure is not allowed to give ownership away. If it did, then it would no longer have ownership of xxx the second time you call the closure, which would be illegal.

That is, you must clone because the closure can be called multiple times.

If the closure is only called once in practice you could use an Option and panic when it's called the second time.

let mut xxx = Some(xxx);
lua.create_function_mut(move |luav, p: (String, u32, Table)| {
    let xxx = xxx.take().unwrap(); // panic on second call
    base(luav, p, xxx)
}).unwrap(),
1 Like

The problem is not moving a value into the closure. You already asked for that and got it. The problem is moving out of the closure. Every time you call base(), you have to provide it a SomeTypeWithoutCopy, so if you don’t clone, then the second time the closure is called, you have nothing left to pass to base.

The reason FnMut is mentioned by the compiler is that if the function were only required to be FnOnce, this wouldn’t be a problem since FnOnces can only be called once — they can give up the values they capture.

Cloning is an appropriate solution here. Or, if the function truly should only be called once, you can put xxx in an Option and take it back out .take().expect("called more than once").

1 Like

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.