Rust中为Iter<&>与Iter<&mut>同时实现Trait的方法
Rust同时支持可变/不可变引用迭代器的Trait实现方案
问题成因
直接为Iterator<Item = &MyObj>和Iterator<Item = &mut MyObj>分别写泛型实现会触发E0119冲突错误,本质是Rust的重叠实现检查不会深入判定&T和&mut T为互斥类型,两个覆盖所有迭代器的宽泛泛型实现会被编译器判定存在潜在交集。如果只保留不可变引用的实现,可变引用迭代器因为Item类型不匹配,调用方法时会触发E0599 Trait约束不满足错误。
错误复现
struct MyObj; trait ConvertIter { fn convert(self); } // 仅写不可变实现时,iter_mut()调用报E0599 impl<I> ConvertIter for I where I: Iterator<Item = &'static MyObj> { fn convert(self) {} } // 放开下面的可变实现会直接报E0119 // impl<I> ConvertIter for I // where I: Iterator<Item = &'static mut MyObj> // { // fn convert(self) {} // } fn main() { let arr = [MyObj]; arr.iter().convert(); // arr.iter_mut().convert(); // 放开就报错 }
推荐实现
方案1:辅助标记Trait统一实现(绝大多数场景适用)
通过一个无额外行为的标记Trait,把可变、不可变引用迭代器统一归为支持转换的迭代器类型,只写一次转换逻辑即可,编译器会自动根据引用类型做借用检查:
struct MyObj; // 标记Trait:所有产出MyObj引用的迭代器都实现这个Trait trait MyObjRefIter: Iterator {} impl<'a, I> MyObjRefIter for I where I: Iterator<Item = &'a MyObj> {} impl<'a, I> MyObjRefIter for I where I: Iterator<Item = &'a mut MyObj> {} trait ConvertIter { fn convert(self); } // 统一为所有符合标记的迭代器实现转换逻辑 impl<I> ConvertIter for I where I: MyObjRefIter { fn convert(self) { for item in self { // 不可变迭代器下item是&MyObj,可变迭代器下item是&mut MyObj // 借用检查器会自动限制操作权限,不需要手动区分类型 let _ = item; } } } fn main() { let mut arr = [MyObj]; arr.iter().convert(); // 正常编译 arr.iter_mut().convert(); // 正常编译 }
方案2:宏生成差异化实现(需要区分可变/不可变逻辑时用)
如果可变、不可变迭代器的转换逻辑完全不同,可以用宏生成两个带高阶生命周期参数的实现,避免泛型重叠:
struct MyObj; trait ConvertIter { fn convert(self); } macro_rules! impl_convert { ($item:ty, $logic:block) => { impl<'a, I> ConvertIter for I where I: Iterator<Item = $item> { fn convert(self) $logic } }; } // 不可变引用逻辑 impl_convert!(&'a MyObj, { for item in self { println!("处理不可变引用"); } }); // 可变引用逻辑 impl_convert!(&'a mut MyObj, { for item in self { println!("处理可变引用"); } }); fn main() { let mut arr = [MyObj]; arr.iter().convert(); arr.iter_mut().convert(); }
该方案不会触发E0119的核心是:impl块上声明的高阶生命周期
'a让两个实现的约束被编译器判定为互斥,不存在重叠覆盖的可能。
内容的提问来源于stack exchange,提问作者Finomnis
相关产品推荐
相关产品推荐

