How to remove 'temporary value dropped while borrowed' in a match

Viewed 81

I'm trying a simple variation of command name and arguments depending on the OS. I have a common set of methods for the stdin/stdout, spawn and expect so I would like not to duplicate them since I may add other OS values later:

let cmd = match env::consts::OS {
    "windows" =>
        Command::new("cmd.exe").arg("/c").arg("dir"),
    "linux" =>
        Command::new("ls").arg("-l"),
    _ => {
        panic!("Unrecognized OS");
    }
};
let proc = cmd
    .stdout(Stdio::piped())
    .spawn()
    .expect("Cannot launch the process");

But I'm getting this error:

error[E0716]: temporary value dropped while borrowed
  --> src\inouts.rs:37:13
   |
35 |     let cmd = match env::consts::OS {
   |         --- borrow later stored here
36 |         "windows" =>
37 |             Command::new("cmd.exe").arg("/c").arg("dir"),
   |             ^^^^^^^^^^^^^^^^^^^^^^^                    - temporary value is freed at the end of this statement
   |             |
   |             creates a temporary which is freed while still in use
   |
   = note: consider using a `let` binding to create a longer lived value

I've tried to use intermediate variables in the match, or to declare cmd before like this:

let mut cmd: &Command;
match env::consts::OS {
    "windows" => {
        cmd = Command::new("cmd.exe").arg("/c").arg("dir")
    }
...

but I can't seem to make it work without copying and duplicating the whole part with proc within the match. So this works but it start to get very long and repetitive:

let proc: Child;
match env::consts::OS {
    "windows" => {
        proc = Command::new("cmd.exe")
            .arg("/c")
            .arg("dir")
            .stdout(Stdio::piped())
            .spawn()
            .expect("Cannot launch the process");
    }
...

Is there a way to overcome the lifetime issue and keep something close to the initial code?

I've check many similar temporary value dropped while borrowed problems but couldn't find any solution.

1 Answers

The issue is that the function arg returns a &mut to its underlying Command, not the owned Command itself. So trying to return the return value of arg from a match arm does indeed return a reference to a Command that exists only within the match arm.

To work around this, you can create the Command, store it (owned) in a variable, and then apply the args.

let mut cmd;
match env::consts::OS {
    "windows" => {
        cmd = Command::new("cmd.exe");
        cmd.arg("/c").arg("dir")
    }
    "linux" => {
        cmd = Command::new("ls");
        cmd.arg("-l")
    }
    _ => {
        panic!("Unrecognized OS");
    }
};

let proc = cmd
    .stdout(Stdio::piped())
    .spawn()
    .expect("Cannot launch the process");
Related