Search code examples
rustclap

cannot move out of *** which is behind a shared reference (Clap)


I'm learning rust and would like to better understand what is happening here. I have the following declared:

pub struct SomeHandler {}

impl SomeHandler {
    pub fn configure(&self, app: &App) {
        app.subcommand(
            SubCommand::with_name("server-status")
                .about("Displays the status of the server")
        );
    }

    pub fn check_match(&self, matches: &ArgMatches) -> bool {
        matches.subcommand_matches("server-status").is_some()
    }

    pub fn handle(&self, arg_match: &ArgMatches) {
        ...
    }
}

And then in main.rs I'm calling it like this:

    let mut app = App::new("My CLI")
        .author("Author <author@email.com>")
        .version("0.1.0")
        .about("Command line interface app");

    let myhandler = SomeHandler {};
    myhandler.configure(&app);

    let matches = app.get_matches();

    let matched = myhandler.check_match(&matches);

    if !matched {
        eprintln!("None of the commands match the provided args.");
        process::exit(1);
    }

    myhandler.handle(&matches);
    process::exit(0);

But I get the following error:

error[E0507]: cannot move out of `*app` which is behind a shared reference
  --> cli\src\some_handler.rs:15:9
   |
15 |         app.subcommand(
   |         ^^^ move occurs because `*app` has type `App<'_, '_>`, which does not implement the `Copy` trait

How do I fix this error? Is there a better way to handle this? I'm trying to build a command line application in rust with multiple commands and options. I don't want to implement it all in a single file. What is a good pattern to follow here?

Any help would be great,

Thanks, Manthan


Solution

  • The subcommand() method consumes the app and returns a new one. This nicely supports chaining, but requires your configure function to also accept an object, and also to return one:

    pub fn configure(&self, app: App<'static, 'static>) -> App<'static, 'static> {
        app.subcommand(
            SubCommand::with_name("server-status")
                .about("Displays the status of the server")
        )
    }
    
    // and in main:
    app = myhandler.configure(app);
    

    It's also possible for configure to take a reference, but then that has to be a mut reference, and you have to call mem::replace to extract the Clap out of the reference, leaving a dummy in its stead, and finally assign it back. If you're really curious, take a look at it here.