为什么火柴的对象是借进来的 rust 呢?

f0brbegy  于 2022-11-12  发布在  其他
关注(0)|答案(2)|浏览(177)

这个代码来自于rustlings的quiz2.rs。我知道for(string,command)中的命令是从向量迭代器中借来的。这个命令是借来的,但是为什么Append(n)中的'n'也是借来的呢?

pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
        // TODO: Complete the output declaration!
        let mut output: Vec<String> = vec![];
        for (string, command) in input.iter() {
            // TODO: Complete the function body. You can do it!
            match command {
                Command::Uppercase => output.push(string.to_uppercase()),
                Command::Trim => output.push(string.trim().to_string()),
                Command::Append(n) => {
                    let can_mv_str = string.to_string() + &"bar".repeat(*n);
                    output.push(can_mv_str);
                }
            }
        }
        output
    }
kh212irz

kh212irz1#

首先,Vec::iter()返回一个对向量元素的 references 的迭代器,即&(String, Command)
然后,每当你为某个特定的结构编写一个模式,比如for循环中的双元素元组(string, command),但输入是对该结构的 * 引用 *,Rust就会“通过”该引用进行匹配,并自动为你 * 提供对元素的引用 *(因为一般情况下不可能 * 不 * 获得引用,因为不是每个类型都是Copy)。
因此,command的类型是&Command。然后match command {也会发生同样的事情,并且match的模式中的每个变量绑定(即n)也将是一个引用。
如果你想避免这种情况,你必须做的是 * 显式地写出与引用的匹配 *:

match command {
    ...
    &Command::Append(n) => {
        let can_mv_str = string.to_string() + &"bar".repeat(n);
        output.push(can_mv_str);
    }
}

或者,您可以将输入解引用到match(只要您不绑定任何非Copy值,这就不一定会尝试移出引用):

match *command {
    ...
    Command::Append(n) => {
        let can_mv_str = string.to_string() + &"bar".repeat(n);
        output.push(can_mv_str);
    }
}

最后,如果您希望 * 完全避免这种魔术 *,并编写一个显式执行整个过程的程序,您还需要调整for模式:

for &(ref string, ref command) in input.iter() {

ref意味着“请不要试图将这个值 * 移出 * 我正在匹配的值;只是给予我一个参考。这在现代的Rust中很少见,因为我所说的自动匹配行为使得它几乎是不必要的。Rust的这个特性被称为“匹配人体工程学”,因为它让你不必一直写大量的&ref。但是,正如你所看到的,它可以导致令人惊讶的行为,旧的显式样式还可以避免处理对Copy类型(如整数)的不必要的引用。
如果您想尝试在不使用匹配人体工程学的情况下编写Rust,以了解“实际发生了什么”,您可以启用Clippy restriction lint 来标记模式实际上不适合其匹配类型的任何位置:


# [warn(clippy::pattern_type_mismatch)]

并运行cargo clippy来查看新的警告。您可能会发现有相当多的模式隐式地作用于引用!

vohkndzv

vohkndzv2#

这是因为RFC 2005。在此之前,当你想在match中引用一个内部字段时,有一个特殊的语法refref mut
这里发生的是你的command是一个&Command。你匹配它,但手臂都是Command。所以command被自动取消引用。但是,在Command::Append中,你获得了内部n的所有权。这是不可能发生的,因为你的command是一个引用。所以rust给你一个对n的引用。
有多种方法可以解决这个问题,最简单的方法是自己解引用n,如下所示:

match command {
    Command::Append(&n) => ..., // n is owned here
    ...
}

nCopy时,这是有效的。如果n不是Copy而是Clone,您也可以在匹配体本身中执行let n = n.clone()
您也可以取得command的拥有权,如下所示:

match *command { // if Command is Copy
   Command::Append(n) => ..., // n is owned here
   ...
}

// OR

match command.clone() { // if Command is Clone
   Command::Append(n) => ..., // n is owned here
   ...
}

如果你不能完成以上任何一项,那么你就需要处理引用本身,或者把你的迭代器从input.iter()改为input.into_iter(),从一开始就得到一个拥有的Command

相关问题