Reputation: 1745
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
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