为什么使用cloned()函数可以使得这个程序编译通过?

15
我开始学习Rust并尝试实现一个反转字符串向量的函数。我找到了一个解决方案,但我不明白为什么它有效。
这个有效:
fn reverse_strings(strings:Vec<&str>) -> Vec<&str> {
  let actual: Vec<_> = strings.iter().cloned().rev().collect();
  return actual;
}

但是这个不行。

fn reverse_strings(strings:Vec<&str>) -> Vec<&str> {
  let actual: Vec<_> = strings.iter().rev().collect(); // without clone
  return actual;
}

错误消息

src/main.rs:28:10: 28:16 error: mismatched types:
 expected `collections::vec::Vec<&str>`,
   found `collections::vec::Vec<&&str>`
(expected str,
    found &-ptr) [E0308]

有人能解释一下为什么吗?第二个函数中发生了什么?谢谢!

1个回答

28
因此,对.cloned()的调用本质上就像在同一位置执行.map(|i| i.clone())(即您可以用后者替换前者)。
问题是当你调用iter()时,你正在迭代/操作被迭代的项目的引用。请注意,向量已经由“引用”组成,具体来说是字符串切片。
因此,为了更详细地了解,让我们将cloned()替换为我上面提到的等效map(),出于教学目的而这样做,因为它们是等效的。 这实际上是它的样子:
.map(|i: & &str| i.clone())

请注意,这是一个对引用(slice)的引用,因为如我所说,iter() 操作的是项目的引用,而不是项目本身。由于被迭代的向量中的单个元素是 &str 类型,因此我们实际上得到了对它的引用,即 & &str。通过在每个项目上调用 clone(),我们从 & &str 转换为 &str,就像在 &i64 上调用 .clone() 会导致 i64 一样。

总之,iter() 迭代元素的引用。因此,如果您使用构造迭代器的方式创建新向量并从迭代器中收集生成的项目,则会得到一个 引用的引用 向量,即:

let actual: Vec<& &str> = strings.iter().rev().collect();

首先要认识到,这个类型并不同于你所说的函数返回类型 Vec<&str>。然而更基本的是,这些引用的生命周期将局限于函数内部,因此,即使你将返回类型改为 Vec<& &str>,你仍将获得生命周期错误。

不过,另一种方法是使用 into_iter() 方法。该方法实际上迭代每个元素,而不是其引用。然而,这意味着元素会从原始的迭代器/容器中被转移。在您的情况下,这种操作是可行的,因为您通过值传递了向量,所以可以将其中的元素移出。

fn reverse_strings(strings:Vec<&str>) -> Vec<&str> {
  let actual: Vec<_> = strings.into_iter().rev().collect();
  return actual;
}

playpen

这种方法比克隆(cloning)更有意义,因为我们是通过值传递向量的,可以对元素进行任何操作,包括将它们移动到不同的位置(在本例中,是新的反转向量)。即使我们不这样做,该函数结束时向量也将被删除,所以我们最好这样做。如果我们不能这样做(例如,如果我们是通过引用传递向量或者传递的是一个切片而不是向量),则克隆会更合适。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接