Rust中ZigzagIterator的iter类型确定及可读性优化问询
Rust ZigzagIterator 类型问题解答
我正在用Rust解决一道面试题,以下是我的解决方案:
struct ZigzagIterator { iter: std::iter::Peekable<Box<dyn Iterator<Item = i32>>>, // 类型声明错误 } impl ZigzagIterator { fn new(v1: Vec<i32>, v2: Vec<i32>) -> Self { let v1_iter: std::vec::IntoIter<_> = v1.into_iter(); let v2_iter: std::vec::IntoIter<_> = v2.into_iter(); let iter = v1_iter.zip(v2_iter).flat_map(|(a,b)| vec![a,b].into_iter()).chain(v1_iter).chain(v2_iter); Self { iter: Box::new(iter).peekable() } } fn next(&self) -> i32 { self.iter.next().unwrap() } fn has_next(&self) -> bool { self.iter.peek().is_some() } }
先指出代码里的两个基础错误:
zip会消耗v1_iter和v2_iter,后续的chain(v1_iter)和chain(v2_iter)会因为迭代器已被移动而编译失败next和has_next方法使用&self是错误的,因为迭代器的next/peek方法需要&mut self
接下来回答你的两个问题:
1. ZigzagIterator 的 iter 成员的确切类型是什么?
如果忽略上述逻辑错误,你的代码中 iter 的实际类型是一个极度冗长的嵌套迭代器类型,包含多层 Chain、FlatMap、Zip 以及匿名闭包类型,具体写法如下(几乎无法手动维护):
std::iter::Peekable< std::iter::Chain< std::iter::Chain< std::iter::FlatMap< std::iter::Zip<std::vec::IntoIter<i32>, std::vec::IntoIter<i32>>, std::vec::IntoIter<i32>, [closure@src/main.rs:10:31: 10:54] >, std::vec::IntoIter<i32> >, std::vec::IntoIter<i32> > >
这种类型包含匿名闭包,无法直接手动声明,这也是你无法通过指定确切类型编译的核心原因。
2. 如何调整代码让类型更易读?
推荐使用动态分发(Box<dyn Iterator>)的方式简化类型,同时修正原有的逻辑错误,实现正确的 zigzag 迭代逻辑:
修正后的代码
struct ZigzagIterator { iter1: std::iter::Peekable<Box<dyn Iterator<Item = i32>>>, iter2: std::iter::Peekable<Box<dyn Iterator<Item = i32>>>, take_first: bool, // 标记下一个要取的是第一个迭代器还是第二个 } impl ZigzagIterator { fn new(v1: Vec<i32>, v2: Vec<i32>) -> Self { Self { iter1: Box::new(v1.into_iter()).peekable(), iter2: Box::new(v2.into_iter()).peekable(), take_first: true, } } // 返回 Option<i32> 比直接 unwrap 更安全,避免空迭代器 panic fn next(&mut self) -> Option<i32> { if self.take_first { // 先尝试取第一个迭代器的元素 if let Some(val) = self.iter1.next() { self.take_first = false; Some(val) } else { // 第一个迭代器为空,直接取第二个的剩余元素 self.iter2.next() } } else { // 尝试取第二个迭代器的元素 if let Some(val) = self.iter2.next() { self.take_first = true; Some(val) } else { // 第二个迭代器为空,直接取第一个的剩余元素 self.iter1.next() } } } fn has_next(&mut self) -> bool { self.iter1.peek().is_some() || self.iter2.peek().is_some() } }
代码说明
- 用两个
Peekable<Box<dyn Iterator<Item = i32>>>分别保存两个输入数组的迭代器,类型清晰易懂 - 通过
take_first标记交替取元素的顺序,实现正确的 zigzag 逻辑 - 将
next的返回值改为Option<i32>,避免空迭代器调用unwrap导致 panic,更符合 Rust 的安全规范
如果不想使用动态分发,也可以用枚举来包装具体的迭代器类型,但代码会稍显繁琐,动态分发的方式在这种场景下已经足够简洁高效。
内容的提问来源于stack exchange,提问作者Wren
相关产品推荐
相关产品推荐

