Rust中reverse_words2的collect函数失效原因及问题解答
我尝试实现字符串单词反转功能(例如将"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(); // 此处报错 👇 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.
问题解答
为什么迭代器返回
&&str而非&str?Vec<&str>调用iter()方法时,会返回一个引用迭代器,它遍历的是容器中元素的引用。因为容器内的元素是&str,所以迭代器产出的就是&(&str),也就是&&str。这是Rust的设计逻辑:iter()用于以引用方式遍历容器,避免转移元素所有权。为什么第一个函数能运行,第二个不行?
第一个函数中,for w in words.iter().rev()拿到的w是&&str,但调用reversed.push(w)时,Rust会自动进行解引用强制转换——Vec<&str>的push方法接受&str类型,而&&str可以被自动解引用为&str,因此编译通过。
第二个函数里,collect()要求迭代器产出的类型必须和目标容器的元素类型严格匹配:迭代器产出&&str,但我们要收集成Vec<&str>,没有自动解引用的步骤,因此类型不匹配报错。join的结果为什么是String而非&str?join方法的作用是将多个字符串切片拼接为一个新字符串,这个过程需要在堆上分配新内存来存储拼接后的完整内容,因此返回的是拥有所有权的String,而非仅作为引用的&str。不存在自动类型转换,join的返回类型本来就是String,刚好和函数的返回类型匹配。
修改第二个函数的方案
无需手动维护循环,有以下几种方式修复:
方案1:使用cloned()复制元素
通过cloned()将&&str转换为&str:
pub fn reverse_words2(input: &str) -> String { let words: Vec<&str> = input.split(" ").collect(); let reversed: Vec<&str> = words.iter().rev().cloned().collect(); reversed.join(" ") }
方案2:使用into_iter()获取元素所有权
into_iter()会直接遍历容器的元素而非引用,这里&str是Copy类型,所有权转移后依然是&str:
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(" ") }
方案3:简化链式调用
甚至不需要显式创建words变量,直接链式操作完成:
pub fn reverse_words2(input: &str) -> String { input.split(" ").rev().collect::<Vec<_>>().join(" ") }
内容的提问来源于stack exchange,提问作者Harshal Patil

