如何为Vec新类型实现IntoIterator且不调用collect()?
问题描述
我写了一个Rust小示例程序:
type Index = usize; type ValType = u32; #[derive(Debug)] pub struct NewVec(pub Vec<ValType>); impl IntoIterator for NewVec { type Item = (Index, ValType); type IntoIter = <Vec<(Index, ValType)> as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0 .into_iter() .enumerate() .collect::<Vec<_>>() .into_iter() } } fn main() { let v = vec![100]; let new_v = NewVec(v.clone()); println!("Vec::into_iter: {:?}", v.into_iter()); println!("NewVec::into_iter: {:?}", new_v.into_iter()); }
程序能正常运行,输出如下:
Vec::into_iter: IntoIter([100]) NewVec::into_iter: IntoIter([(0, 100)])
但NewVec的into_iter方法调用了collect()创建新Vec再转迭代器,我想去掉collect()直接返回迭代器,避免不必要的内存分配。但移除collect()后会报类型不匹配错误:
error[E0308]: mismatched types --> src/main.rs:12:9 | 11 | fn into_iter(self) -> Self::IntoIter { | -------------- expected `std::vec::IntoIter<(usize, u32)>` because of return type 12 | / self.0 13 | | .into_iter() 14 | | .enumerate() 15 | | .into_iter() | |________________________^ expected `IntoIter<(usize, u32)>`, found `Enumerate<IntoIter<u32>>` | = note: expected struct `std::vec::IntoIter<(usize, u32)>` found struct `Enumerate<std::vec::IntoIter<u32>>`
我不知道能不能把Enumerate转换成std::vec::IntoIter,或者有没有其他实现方式达成目标?
解决方法
完全可行,根本不需要把Enumerate转换成Vec的迭代器,只要调整IntoIterator实现里的关联类型即可。
原来的代码里type IntoIter错误绑定到了Vec<(Index, ValType)>的迭代器类型,但enumerate()返回的Enumerate<IntoIter<ValType>>本身就是合法的迭代器,完全可以直接作为IntoIter的类型。
修改后的代码如下:
type Index = usize; type ValType = u32; #[derive(Debug)] pub struct NewVec(pub Vec<ValType>); impl IntoIterator for NewVec { type Item = (Index, ValType); // 直接使用enumerate返回的迭代器类型 type IntoIter = std::iter::Enumerate<std::vec::IntoIter<ValType>>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter().enumerate() } } fn main() { let v = vec![100]; let new_v = NewVec(v.clone()); println!("Vec::into_iter: {:?}", v.into_iter()); println!("NewVec::into_iter: {:?}", new_v.into_iter()); }
运行后输出和之前一致,且完全避免了额外的内存分配。
核心逻辑是:IntoIterator trait只要求IntoIter是一个实现了Iterator的类型,并不强制它必须是Vec的迭代器。你之前因错误绑定返回类型,才不得不通过collect()凑类型,现在直接指定正确的迭代器类型就能解决问题。
如果不想写冗长的类型名,还可以在Rust 1.63+版本中使用impl Iterator<Item = (Index, ValType)>作为IntoIter类型,代码更简洁:
type Index = usize; type ValType = u32; #[derive(Debug)] pub struct NewVec(pub Vec<ValType>); impl IntoIterator for NewVec { type Item = (Index, ValType); type IntoIter = impl Iterator<Item = Self::Item>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter().enumerate() } } fn main() { let v = vec![100]; let new_v = NewVec(v.clone()); println!("Vec::into_iter: {:?}", v.into_iter()); println!("NewVec::into_iter: {:?}", new_v.into_iter()); }
这种写法会让编译器自动推导具体的迭代器类型,可读性更强。
内容的提问来源于stack exchange,提问作者danda
相关产品推荐
相关产品推荐

