1
votes

I am writing code to parse CLI arguments and am stuck on quite a common Rust error.

What change (ideally without needing to assign everything to individual let variables, do I need to make to fix the compilation error?

I have been looking at a lot of similar posts as well as re-reaeding the chapter on Ownership in the rust book. However I haven't found a solution to my specific situation.

My code to parse the args is as follows:

The full file on github is here:

pub fn new<'a>(
        args: env::Args,
        default_world_def: Config<'static>,
        presets: Vec<&'static str>,
    ) -> Result<Config<'a>, String> {

        let args2 = args;
        let args_vec = args2.collect::<Vec<String>>();
        let args_slice = args_vec.as_slice();

        let result = match args_slice {
            [_, preset] => {
                if preset == "help" {
                    Err(format!(
                        "try: gol
try: gol {:?}
try: gol [width height num_starting_cells seed display_dead display_alive] (e.g: gol 40 40 40 4045)",
                        presets
                    ))
                } else if presets.contains(&&preset[..]) {
                    Ok(Config::Preset {
                        key: preset.to_string(),
                    })
                } else {
                    Err(format!("Unknown preset, choose from {:?}.", presets))
                }
            }
            [_] => Ok(default_world_def),
            [_, w, h, n, s] => {
                let (width, height, num_starting_cells, seed) = Config::parse_args(&w, &h, &n, &s)?;

                Ok(Config::WorldDef {
                    width,
                    height,
                    num_starting_cells,
                    seed,
                    dead_char: None,
                    alive_char: None,
                })
            }
            [_, w, h, n, s, d, a] => {
                let (width, height, num_starting_cells, seed) = Config::parse_args(&w, &h, &n, &s)?;

                Ok(Config::WorldDef {
                    width,
                    height,
                    num_starting_cells,
                    seed,
                    dead_char: Some(&d),
                    alive_char: Some(&a),
                })
            }
            args => Err(format!(
                "Expected at least 4 or 6 args but got {}",
                args.len() - 1
            ))
        };

        result

This results in the error:

error[E0515]: cannot return value referencing local variable `args_vec`
  --> src/config.rs:77:9
   |
27 |         let args_slice = args_vec.as_slice();
   |                          -------- `args_vec` is borrowed here
...
77 |         result
   |         ^^^^^^ returns a value referencing data owned by the current function

I have tried:

  • modifying lifetime parameters (making more things static)
  • assigning the result of each method call to a variable (see args2, args_vec and args_slice above)
  • making the parameter args: env::Args to the new function a reference (&)

As I understand the issue is that result references one/all of the args variables, which go out of scope and are dropped at the end of the new method which the result relies on in the future.

1
Will do! I will also post a github link to the full file. - tjheslin1

1 Answers

2
votes

When match gives Ok(Config::WorldDef { ... dead_char: Some(&d), alive_char: Some(&a), } the two last references are slices from Strings owned by args_vec (accessed via args_slice).

When the function exits, args_vec does not exist any more, thus the references in the result would be dangling if Rust didn't complain.

I suggest that you store Strings instead of &str in the dead_char and alive_char member; this way, the result could take ownership of these to strings from args_vec. By the way, if you do that, I'm not certain args_slice is useful; I think you can just consume args_vec in the match statement because it won't be used afterwards.