marshallm
marshallm

Reputation: 1745

Mismatched types when building a std::process::Command in a loop

I'm new to Rust, trying to learn safe programming by working with the borrow checker. One thing I tried was to construct a std::process::Command based on input.

If I just wanted to do what all the examples in the documentation assume I want to do and just run a command with arguments that I know at coding time, it works fine:

use std::process::Command;

fn main() {
    let mut command = Command::new("/usr/bin/x-terminal-emulator")
                              .arg("-e")
                              .arg("editor")
                              .output()
                              .unwrap();
}

I'm trying to run a command that I build at runtime. In order to do this, I need to separate the construction of the Command struct from the building of its arguments. When I do it this way, the compiler complains about mismatched types:

use std::env::args;
use std::process::Command;

fn main() {
    let args = args().collect::<Vec<_>>();
    let mut command = Command::new("/usr/bin/x-terminal-emulator");

    for arg in &args[1..args.len()] {
        command = command.arg(arg);
    }
}

The error I get is

mismatched types: expected std::process::Command, found &mut std::process::Command

Looking at the documentation for std::process::Command::arg, it says that it expects a &mut self and returns a &mut Command. According to the compiler, that's exactly what it's getting. Is the documentation wrong, or (far more likely) am I misunderstanding something here?

Upvotes: 3

Views: 764

Answers (1)

Shepmaster
Shepmaster

Reputation: 430791

If we check out the full error provided by the compiler, that may help shed light:

error: mismatched types:
 expected `std::process::Command`,
    found `&mut std::process::Command`
(expected struct `std::process::Command`,
    found &-ptr) [E0308]

command = command.arg(arg);
          ^~~~~~~~~~~~~~~~

The compiler is pointing to the entire right hand side of the expression. This indicates that something is wrong with the return value of the arg call. Let's check out the documentation for arg:

fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command

So, arg operates on a mutable reference to self, accepts an arg parameter, and returns a mutable reference to itself. Let's change your code a bit to test our hypothesis. We will use a trick to make the compiler tell us the types of variables:

for arg in &args[1..args.len()] {
    let _: () = command;
    let _: () = command.arg(arg);
}

This yields the errors:

error: mismatched types:
 expected `()`,
    found `std::process::Command`

let _: () = command;
            ^~~~~~~

error: mismatched types:
 expected `()`,
    found `&mut std::process::Command`

let _: () = command.arg(arg);
            ^~~~~~~~~~~~~~~~

Hah, we got it! We are trying to store a &mut Command into a variable of type Command. Not gonna work! Since this instance of the builder pattern mutates the builder, we don't have to do anything special to preserve it:

use std::env;
use std::process::Command;

fn main() {
    let mut command = Command::new("/usr/bin/x-terminal-emulator");

    for arg in env::args().skip(1) {
        command.arg(arg);
    }
}

There is another style of the builder pattern that accepts self by value and the returns it by value. In that case, you would have to keep track of the builder between each step, and your code would have worked as-is.

Upvotes: 3

Related Questions