为什么collect函数不能在给定的rust代码中工作?

ulydmbyx  于 2023-06-23  发布在  其他
关注(0)|答案(2)|浏览(113)

我正在尝试反转给定字符串中的单词。例如-“John is amazing”将返回“amazing is John”。下面的代码片段可以工作:

pub fn reverse_words1(input: &str) -> String {

    let words: Vec<&str> = input.split(" ").collect();
    let mut reversed: Vec<&str> = Vec::new();

    for w in words.iter().rev() {
        reversed.push(w);
    }

    reversed.join(" ")
}

但是,以下方法行不通:

pub fn reverse_words2(input: &str) -> String {

    let words: Vec<&str> = input.split(" ").collect();

    // Error here 👇
    let reversed: Vec<&str> = words.iter().rev().collect();

    reversed.join(" ")
}

错误是:

a value of type `Vec<&str>` cannot be built from an iterator over elements of type `&&str`.

我这里有三个问题:
1.为什么迭代器提供对&str的引用,而不是直接访问&str,因为它已经是对字符串的切片/引用?
1.为什么第一个函数起作用而第二个函数不起作用?
1.函数的返回类型是String,而我试图联接Vec<&str>。难道join不应该导致&str而不是String类型吗?铁 rust 是否做价值的自动转换?
最后,如何修复第二个函数,以便不必维护自己的for结构?

2uluyalo

2uluyalo1#

正如Alexey所解释的,Vec<T>::iter生成一个项类型为&T的迭代器。因为你有一个Vec<&str>,这意味着它产生的迭代器具有&&str类型。
你可以通过使用into_iter来解决这个问题:

pub fn reverse_words2(input: &str) -> String {

    let words: Vec<&str> = input.split(" ").collect();

    let reversed: Vec<&str> = words.into_iter().rev().collect();

    reversed.join("")
}

但最好避免在一开始就直接从反向迭代器中收集来创建向量:

pub fn reverse_words3(input: &str) -> String {
    let words = input.rsplit(" "); // notice `rsplit` rather than `split`
    let reversed: Vec<&str> = words.collect();
    reversed.join(" ")
}
fafcakar

fafcakar2#

当你传递一个值给一个函数时,如果需要的话,它会自动被取消引用。在for循环中:

for w in words.iter().rev() {
    reversed.push(w);
}

w的类型为&&str,它会自动解引用到&str中,以匹配push所需的类型。
您可以通过添加copied来修复第二个。

let reversed: Vec<&str> = words.iter().rev().copied().collect();

对于为什么得到String而不是&str,这是因为join的返回取决于项目的类型和分隔符的类型。

pub fn join<Separator>(
    &self,
    sep: Separator
) -> <[T] as Join<Separator>>::Output

Join的文档中,有一个[&str]和分隔符&str的实现,以输出String

impl<S: Borrow<str>> Join<&str> for [S] {
    type Output = String;

Borrow<str>是另一个trait,但这里唯一相关的是&str实现了Borrow<str>
Join trait已经这样实现了,因为它需要一个可变的、可增长的类型来放入数据。它不能返回&str,因为它可以访问的(在切片中)是不可变的,而且因为它是一个引用,它不能创建一个新的引用,因为这需要函数外部的东西来拥有它。String解决了这些问题。
请注意,当您提供charsplit而不是&str时,您可以反向迭代它,因此实际上不需要中间的Vec

let reversed: Vec<&str> = input.split(' ').rev().collect();

然后你可以直接收集到一个String

pub fn reverse_words2(input: &str) -> String {
    input.split(' ').rev().collect()
}

如果你需要一个非空的分隔符,你可以做一些迭代器体操。

// Adds ", " between each word
pub fn reverse_words2(input: &str) -> String {
    input
        .split(' ')
        .rev()
        .flat_map(|s| [", ", s])
        .skip(1)
        .collect()
}

相关问题