Difference between (|| var) and (move || var.clone())

Why does (move || args.path.clone()) work, but (|| args.path) doesn't in the following code? What's the difference between them?

cargo add bevy

use std::path::PathBuf;

use bevy::prelude::*;

struct Args {
    path: PathBuf,
}

fn main() {
    let args: Args = Args { path: default() };

    App::new()
        .add_plugins(MinimalPlugins)

        /*
        error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
          --> src/main.rs:29:32
           |
        29 |         .add_systems(Startup, (|| args.path).pipe(startup))
           |                               -^^----------- ---- required by a bound introduced by this call
           |                               ||  |
           |                               ||  closure is `FnOnce` because it moves the variable `args.path` out of its environment
           |                               |this closure implements `FnOnce`, not `FnMut`
           |                               the requirement to implement `FnMut` derives from here
           |
           = note: required for `&'a mut {closure@src/main.rs:29:32: 29:34}` to implement `for<'a> FnOnce()`
           = note: required for `{closure@src/main.rs:29:32: 29:34}` to implement `bevy::prelude::SystemParamFunction<fn() -> PathBuf>`
           = note: required for `{closure@src/main.rs:29:32: 29:34}` to implement `bevy::prelude::IntoSystem<(), PathBuf, (IsFunctionSystem, fn() -> PathBuf)>`
         */
        //.add_systems(Startup, (|| args.path).pipe(startup))

        // This works.
        .add_systems(Startup, (move || args.path.clone()).pipe(startup))

        .run();
}

fn startup(In(path): In<PathBuf>) {
    println!("{path:?}");
}

(|| args.path) takes ownership of args.path when constructed, then gives it away when called. If called a second time, it no longer has anything to give away, so it may not be called a second time (it does not implement FnMut or Fn).

(move || args.path.clone()) takes ownership of args.path when constructed, then clones it when called. It can therefore be called any number of times (it implements FnMut and Fn).

7 Likes

Thanks. I decided to use a function that returns a closure so that clone() is no longer necessary.

fn main() {
    // ...
    .add_systems(Startup, create_startup_system(args.path))
    .run();
}

fn create_startup_system(path: PathBuf) -> impl Fn(Commands<'_, '_>) {
    move |mut commands: Commands| {
        commands.spawn(Name::new(path.display().to_string()));
    }
}

What about (|| args.path.clone())? Does the closure still take ownership and can be called multiple times?

In that case it can be called multiple times, but it does not take ownership; it borrows args.path, because that is all that is needed to be able to call clone().

2 Likes

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.