关于对任意迭代器的引用进行多次遍历的Rust技术问询
嘿,我看到你这段代码已经能正常运行了——它通过AsRef<[&'static str]>这个约束,让函数可以接受Vec、数组这类能转换成切片的类型,而切片本身是支持多次遍历的,所以你能在循环里反复检查输入是否匹配单词。
不过看你没写完的句子,应该是想调整成支持任意迭代器的引用,同时能多次遍历对吧?毕竟普通的迭代器只能被遍历一次,直接用的话会遇到所有权或者无法重复遍历的问题,这里给你几个实用的解决方案:
方案1:要求迭代器实现Clone
如果你的输入迭代器是可以被克隆的,那每次需要遍历的时候就克隆一个新的迭代器实例出来。这样修改函数约束:
fn do_it_fun<I>(words: Vec<&str>, inputs: I) where I: Iterator<Item = &'static str> + Clone, { for word in words { if inputs.clone().any(|i| i == word) { println!("Match: {}", word); } } } fn main() { let input = vec!["foo", "bar"].into_iter(); let input2 = ["bar", "baz"].into_iter(); do_it_fun(vec!["bar"], input); do_it_fun(vec!["baz"], input2); }
不过要注意,不是所有迭代器都能实现Clone,比如一些消耗型的迭代器(比如读取文件的迭代器)就不行,这个方案有一定局限性。
方案2:预先收集迭代器元素到容器里
这是更通用的方案:在函数开头把迭代器的所有元素收集到一个Vec或者其他容器中,之后就可以反复遍历这个容器了。这样不管是什么迭代器,只要能产出元素就能用:
fn do_it_fun<I>(words: Vec<&str>, inputs: I) where I: IntoIterator<Item = &'static str>, { // 先把迭代器的元素收集到Vec里,之后就能反复遍历 let input_items: Vec<_> = inputs.into_iter().collect(); for word in words { if input_items.iter().any(|&i| i == word) { println!("Match: {}", word); } } } fn main() { let input = vec!["foo", "bar"]; let input2 = ["bar", "baz"]; do_it_fun(input, input2); }
代价是需要额外的内存来存储元素,但胜在兼容性极强,几乎能适配所有迭代器类型。
方案3:保留原有的AsRef约束(最省心)
其实你原来的写法已经很实用了——AsRef<[T]>能覆盖绝大多数常见的集合类型(Vec、数组、&[T]切片等),这些类型本身就支持多次遍历,而且不需要额外的内存开销。如果你的使用场景里不需要处理特别复杂的迭代器,这个方案是最优的。
另外,顺便提个小优化:可以把words的参数类型从Vec<&str>改成impl IntoIterator<Item = &str>,这样函数能接受更多类型的单词集合,比如直接传数组或者切片,灵活性更高:
fn do_it_fun<I, J>(words: J, inputs: I) where I: AsRef<[&'static str]>, J: IntoIterator<Item = &str>, { for word in words { if inputs.as_ref().into_iter().any(|i| *i == word) { println!("Match: {}", word); } } }
最后补充一点:Rust里的迭代器本质是“一次性消耗”的,所以要实现多次遍历,要么让迭代器可克隆,要么把元素提前存下来,要么使用本身支持重复遍历的类型(比如切片、集合),根据你的实际场景选最合适的就好啦。
内容来源于stack exchange

