Rust中如何统一不同字符串集合的迭代器实现单循环遍历?
统一不同集合的非消耗型迭代器
核心问题分析
HashSet<String>.iter() 返回的是 Iter<'_, String>,而 HashMap<String, i32>.keys() 返回的是 Keys<'_, String, i32>——这两个是完全不同的具体类型,直接通过if-else赋值会触发类型不匹配错误。要统一它们,需要利用Rust的trait对象特性,因为两者都实现了 Iterator<Item = &String> trait。
解决方案1:使用 trait 对象(Box)
将两种迭代器装箱为统一的trait对象,就能实现类型兼容,且全程是非消耗型(仅引用集合元素,不转移所有权):
use std::collections::{HashMap, HashSet}; fn main() { run(true); run(false); } fn run(some_argument: bool) { let set: HashSet<String> = (0..4).map(|v| v.to_string()).collect(); let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect(); let iter: Box<dyn Iterator<Item = &String>> = if some_argument { Box::new(set.iter()) } else { Box::new(map.keys()) }; for v in iter { println!("{v}"); } }
如果想直接得到&str类型的迭代项,只需在迭代器后追加.map(|s| s.as_str())转换即可:
let iter: Box<dyn Iterator<Item = &str>> = if some_argument { Box::new(set.iter().map(|s| s.as_str())) } else { Box::new(map.keys().map(|s| s.as_str())) };
解决方案2:使用泛型函数(零运行时开销)
若对性能敏感,可将遍历逻辑抽为泛型函数,让编译器为两种迭代器类型生成静态分发的具体实现,完全避免trait对象的微小运行时开销:
use std::collections::{HashMap, HashSet}; use std::iter::Iterator; fn main() { run(true); run(false); } fn run(some_argument: bool) { let set: HashSet<String> = (0..4).map(|v| v.to_string()).collect(); let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect(); if some_argument { process_iter(set.iter()); } else { process_iter(map.keys()); } } fn process_iter<I>(iter: I) where I: Iterator<Item = &'_ String>, { for v in iter { println!("{v}"); } }
附加问题:集合存储&str时的处理
如果其中一个集合存储的是&str(比如HashSet<&str>),依然可以在无额外内存分配的前提下实现迭代器统一:
方案1:trait 对象实现
use std::collections::{HashMap, HashSet}; fn main() { run(true); run(false); } fn run(some_argument: bool) { let static_strs = ["0", "1", "2", "3"]; let set: HashSet<&str> = static_strs.iter().copied().collect(); let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect(); let iter: Box<dyn Iterator<Item = &str>> = if some_argument { Box::new(set.iter().copied()) } else { Box::new(map.keys().map(|s| s.as_str())) }; for v in iter { println!("{v}"); } }
这里set.iter()返回Iter<'_, &str>,copied()将&&str转换为&str,和map.keys().map(...)的输出类型完全统一,全程无内存分配。
方案2:泛型函数实现
use std::collections::{HashMap, HashSet}; use std::iter::Iterator; fn main() { run(true); run(false); } fn run(some_argument: bool) { let static_strs = ["0", "1", "2", "3"]; let set: HashSet<&str> = static_strs.iter().copied().collect(); let map: HashMap<String, i32> = (5..9).map(|v| (v.to_string(), v)).collect(); if some_argument { process_str_iter(set.iter().copied()); } else { process_str_iter(map.keys().map(|s| s.as_str())); } } fn process_str_iter<I>(iter: I) where I: Iterator<Item = &'_ str>, { for v in iter { println!("{v}"); } }
同样是静态分发,零额外开销,直接处理&str类型的迭代项。
内容的提问来源于stack exchange,提问作者Yuri Astrakhan
相关产品推荐
相关产品推荐

