在循环中构建 std::process::Command 时类型不匹配

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

我是 Rust 的新手,正在尝试通过使用借用检查器来学习安全编程。我尝试的一件事是根据输入构造一个 std::process::Command

如果我只是想执行文档中所有示例假设我想执行的操作,并且只是 运行 一个带有我在编码时知道的参数的命令,它工作正常:

use std::process::Command;

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

我正在尝试 运行 我在 运行 时构建的命令。为此,我需要将 Command 结构的构造与其参数的构造分开。当我这样做时,编译器抱怨类型不匹配:

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);
    }
}

我得到的错误是

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

查看 std::process::Command::arg 的文档,它说它需要一个 &mut self 和 returns 一个 &mut Command。根据编译器的说法,这正是它得到的。文档有误吗,或者(更有可能)我误解了什么?

如果我们检查编译器提供的完整错误,这可能有助于阐明:

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

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

编译器指向表达式的整个右侧。这表明 arg 调用的 return 值 有问题。让我们看看 documentation for arg:

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

因此,argself 的可变引用进行操作,接受 arg 参数,并且 returns对自身的可变引用。让我们稍微更改您的代码以检验我们的假设。我们将使用一个技巧 make the compiler tell us the types of variables:

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

这会产生错误:

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

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

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

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

哈,我们明白了!我们正在尝试将 &mut Command 存储到 Command 类型的变量中。不会工作!由于构建器模式的这个实例改变了构建器,我们不需要做任何特殊的事情来保留它:

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);
    }
}

还有另一种样式的构建器模式,它按值接受 self 并按值接受 return。在这种情况下,您 必须在每个步骤之间跟踪生成器,并且您的代码将按原样运行。