How to idiomatically parse clap's matches, from a lifetime perspective?

In a program of mine, I'd like to isolate the arguments parsing (performed via clap) in a function.

The version I've written works fine. If a better logic saved 200 nanoseconds or 50 bytes, in the context of a regular application I wouldn't care, but since I'm a beginner learning Rust, it's imporant for me to understand how the language works exactly.

The working version is this:

// Receives `env::args().collect()`
// Alternatively, don't receive any argument, and use `get_matches()`.
//
fn parse_commandline_arguments(args: &Vec<String>) -> Vec<String> {
    let matches = App::new("test")
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name("INTERVALS")
                .required(true)
                .index(1)
                .multiple(true),
        )
        .get_matches_from(args);

    matches
        .values_of("INTERVALS")
        .unwrap()
        .map(|str|String::from(str))
        .collect::<Vec<String>>()
}

Essentially, I workaround lifetime/borrowing issues by copying the strings (string slices).

Originally though, I tried not to perform such conversion:

fn parse_commandline_arguments<'a>() -> Vec<&'a str> {
  let matches = App::new("compute_hours")
      .setting(AppSettings::TrailingVarArg)
      .arg(
          Arg::with_name("INTERVALS")
              .required(true)
              .index(1)
              .multiple(true),
      )
      .get_matches();

  // `matches` doesn't live long enough
  //
  matches
      .values_of("INTERVALS")
      .unwrap()
      .collect::<Vec<&'a str>>()
}

and all hell broke loose.

Is there any way of making the latter version work? Is it more idiomatic? Is there any more idiomatic way?

Thanks!

This will never work, because matches gets dropped at the end of the function. You cannot have a reference to data that no longer exists - in other languages, that would cause a use-after-free/null pointer exception, but in Rust, it's a compile error, signified by the "doesn't live long enough" that you're seeing.

A better approach might be to have the function return matches (which is of type ArgMatches). You could then either call values_of directly on the returned value in the caller function, or you could have some other helper functions which take matches as a reference and read data from it.

Have you considered using structopt? For a while I was using clap and ran in to similar issues. Structopt uses clap under the hood and does all the encapsulating for you!

Thanks for the suggestion! I'm likely to stick to clap. The reason is that parsing in itself is a solved problem in my case - my objective was to have a better understanding of Rust :slight_smile:

Thanks! I've modified the code to be around the lines of:

let args = std::env::args().collect::<Vec<String>>();
let matches = parse_args(&args);
let options = extract_options(&matches);

which, I suppose, is now a proper Rust design.

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.