1

I'm trying to store clap::ArgMatches in a struct like so:

struct Configurator {
    cli_args: ArgMatches,
    root_directory: String
}

The error I receive is:

error[E0106]: missing lifetime specifier
 --> src/configurator.rs:5:15
  |
5 |     cli_args: ArgMatches,
  |               ^^^^^^^^^^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
4 | struct Configurator<'a> {
5 |     cli_args: ArgMatches<'a>,
  |

I tried the suggested solution that is given in the error output, but that appears to cause different errors.

Here's a little more context:

extern crate clap;
use clap::{Arg, App, ArgMatches};

struct Configurator {
    cli_args: ArgMatches,
    root_directory: String
}

impl Configurator {
    pub fn build() -> Configurator {
        let configurator = Configurator {};

        // returns ArgMatches apparently 
        let cli_args = App::new("Rust Web Server")
            .version("0.0.1")
            .author("Blaine Lafreniere <brlafreniere@gmail.com>")
            .about("A simple web server built in rust.")
            .arg(Arg::with_name("root_dir")
                .short("r")
                .long("root_dir")
                .value_name("ROOT_DIR")
                .help("Set the root directory that the web server will serve from.")
                .takes_value(true))
            .get_matches();
        
        configurator.cli_args = cli_args;
    }
}
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Blaine Lafreniere
  • 3,451
  • 6
  • 33
  • 55
  • It looks like your question might be answered by the answers of [error[E0106\]: missing lifetime specifier (despite it being set)](https://stackoverflow.com/q/54217705/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Dec 23 '20 at 17:59
  • @Shepmaster tried the solution suggested in that link and no, it does not solve the issue. – Blaine Lafreniere Dec 23 '20 at 18:01
  • 2
    Can you please update your answer to show how you're constructing `clap::App` and also what you've tried so far? – pretzelhammer Dec 23 '20 at 18:03
  • @Shepmaster Here's the deal: if I knew what was going on and how to fix this code, and why it was different than the link you just linked, I wouldn't be here on stackoverflow asking questions. – Blaine Lafreniere Dec 23 '20 at 18:04
  • 1
    Just a precision: `App::new()` returns a `App` struct; then you can parse the actual cli arguments with `your_app_instance.get_matches()`, which returns a `ArgMatches` instance. – yolenoyer Dec 23 '20 at 18:09
  • 2
    Adding the lifetime specifier as the error message suggests seems to work just fine [on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=20d2194069ef53865e8f1d426a4297ca). Can you please explain what exactly was not working for you? – Ibraheem Ahmed Dec 23 '20 at 18:09
  • @yolenoyer I've added some more context – Blaine Lafreniere Dec 23 '20 at 18:11

2 Answers2

2

As the error message suggests, you have to add named lifetime specifiers to Configurator. This is because ArgMatches holds references to values, so you must tell Rust how long those references will live:

struct Configurator<'a> {
    cli_args: ArgMatches<'a>,
    root_directory: String
}

You must do the same for the impl block:

impl<'a> Configurator<'a> {
    pub fn build() -> Configurator<'a> {
      // ...
    }
}

The other issue with your code is that in Rust, you must initialize all fields when instantiating a struct:

// this doesn't work
let config = Configurator {}

// but this does
let config = Configurator {
  cli_args: cli_args, 
  root_directory: "/home".to_string(),
};

Finally, you have to return the Configurator from the function:

pub fn build() -> Configurator<'a> {
    let cli_args = App::new("Rust Web Server")
        .version("0.0.1")
        .author("Blaine Lafreniere <brlafreniere@gmail.com>")
        .about("A simple web server built in rust.")
        .arg(
            Arg::with_name("root_dir")
                .short("r")
                .long("root_dir")
                .value_name("ROOT_DIR")
                .help("Set the root directory that the web server will serve from.")
                .takes_value(true),
        )
        .get_matches();

    return Configurator {
        cli_args: cli_args,
        root_directory: "/home".to_string(),
    };
}

Here is a runnable example.

vallentin
  • 23,478
  • 6
  • 59
  • 81
Ibraheem Ahmed
  • 11,652
  • 2
  • 48
  • 54
1

Since you build the App using only 'static strings then the return type of .arg_matches() is ArgMatches<'static> which you should explicitly state in your Configurator struct definition. This is much better than using a generic lifetime parameter like 'a since it doesn't "infect" your Configurator struct with any lifetime annotations. Also, you cannot "partially" initialize structs in Rust, they must be fully initialized, so I've moved the construction of the Configurator struct to the bottom of the build function when all data is ready.

use clap::{Arg, App, ArgMatches};

struct Configurator {
    cli_args: ArgMatches<'static>,
    root_directory: String
}

impl Configurator {
    pub fn build(root_directory: String) -> Configurator {
        let cli_args = App::new("Rust Web Server")
            .version("0.0.1")
            .author("Blaine Lafreniere <brlafreniere@gmail.com>")
            .about("A simple web server built in rust.")
            .arg(Arg::with_name("root_dir")
                .short("r")
                .long("root_dir")
                .value_name("ROOT_DIR")
                .help("Set the root directory that the web server will serve from.")
                .takes_value(true))
            .get_matches();
        
        Configurator {
            cli_args,
            root_directory,
        }
    }
}

playground

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98