如何为Rust容器与迭代器实现通用的单调判断Trait?
如何用单个Trait同时适配容器与迭代器判断元素单调性?
我想实现一个Trait,用来判断容器或迭代器的元素是否单调。目前用了MonotonicIterator和Monotonic两个Trait,算法没重复实现,但Trait定义冗余。尝试合并成单个Trait时编译器报实现冲突,想问能不能只用一个Trait同时适配容器和迭代器?
当前冗余实现代码
pub trait MonotonicIterator<T> { fn is_monotonic(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_down(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_up(self, cmp: &dyn Comparator<T>) -> bool; } impl<'a,T,I> MonotonicIterator<&'a T> for I where I: std::iter::Iterator<Item=&'a T> + Clone { ... } pub trait Monotonic<T> { fn is_monotonic(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_down(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_up(self, cmp: &dyn Comparator<T>) -> bool; } impl<'a,'b:'a,T:'b,C> Monotonic<&'a T> for &'b C where &'b C: IntoIterator<Item=&'a T>, <&'b C as IntoIterator>::IntoIter: Clone { fn is_monotonic(self, cmp: &dyn Comparator<&'a T>) -> bool { return MonotonicIterator::is_monotonic_up(self.into_iter(), cmp) || MonotonicIterator::is_monotonic_down(self.into_iter(), cmp); } fn is_monotonic_down(self, cmp: &dyn Comparator<&'a T>) -> bool { return MonotonicIterator::is_monotonic_down(self.into_iter(), cmp); } fn is_monotonic_up(self, cmp: &dyn Comparator<&'a T>) -> bool { return MonotonicIterator::is_monotonic_up(self.into_iter(), cmp); } }
尝试合并后的冲突代码
pub trait Monotonic<T> { fn is_monotonic(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_down(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_up(self, cmp: &dyn Comparator<T>) -> bool; } impl<'a,T,I> Monotonic<&'a T> for I where I: std::iter::Iterator<Item=&'a T> + Clone { ... } impl<'a,'b:'a,T:'b,C> Monotonic<&'a T> for &'b C where &'b C: IntoIterator<Item=&'a T>, <&'b C as IntoIterator>::IntoIter: Clone { fn is_monotonic(self, cmp: &dyn Comparator<&'a T>) -> bool { return self.into_iter().is_monotonic(cmp); } fn is_monotonic_down(self, cmp: &dyn Comparator<&'a T>) -> bool { return self.into_iter().is_monotonic_down(cmp); } fn is_monotonic_up(self, cmp: &dyn Comparator<&'a T>) -> bool { return self.into_iter().is_monotonic_up(cmp); } }
解决方法:用类型约束区分迭代器与容器
可以通过给容器的实现添加排除迭代器类型的约束,让两个impl的匹配范围完全不重叠,从而解决冲突。
修改后的完整代码
// 假设你已经定义了Comparator trait pub trait Comparator<T> { fn compare(&self, a: &T, b: &T) -> std::cmp::Ordering; } pub trait Monotonic<T> { fn is_monotonic(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_down(self, cmp: &dyn Comparator<T>) -> bool; fn is_monotonic_up(self, cmp: &dyn Comparator<T>) -> bool; } // 给所有符合条件的迭代器实现Monotonic impl<'a, T, I> Monotonic<&'a T> for I where I: std::iter::Iterator<Item = &'a T> + Clone, { fn is_monotonic(self, cmp: &dyn Comparator<&'a T>) -> bool { self.clone().is_monotonic_up(cmp) || self.is_monotonic_down(cmp) } fn is_monotonic_down(self, cmp: &dyn Comparator<&'a T>) -> bool { self.clone().zip(self.skip(1)).all(|(a, b)| cmp.compare(a, b) != std::cmp::Ordering::Less) } fn is_monotonic_up(self, cmp: &dyn Comparator<&'a T>) -> bool { self.clone().zip(self.skip(1)).all(|(a, b)| cmp.compare(a, b) != std::cmp::Ordering::Greater) } } // 给所有符合条件的容器实现Monotonic,添加!Iterator约束避免冲突 impl<'a, 'b: 'a, T: 'b, C> Monotonic<&'a T> for &'b C where &'b C: IntoIterator<Item = &'a T> + !std::iter::Iterator, // 核心:排除迭代器类型 <&'b C as IntoIterator>::IntoIter: Clone, { fn is_monotonic(self, cmp: &dyn Comparator<&'a T>) -> bool { self.into_iter().is_monotonic(cmp) } fn is_monotonic_down(self, cmp: &dyn Comparator<&'a T>) -> bool { self.into_iter().is_monotonic_down(cmp) } fn is_monotonic_up(self, cmp: &dyn Comparator<&'a T>) -> bool { self.into_iter().is_monotonic_up(cmp) } }
原理说明
冲突的根源是:所有迭代器都自动实现了IntoIterator(返回自身),所以迭代器类型同时满足两个impl的约束条件,编译器无法确定用哪个实现。
添加!std::iter::Iterator约束后,第二个impl只会匹配不是迭代器但能转换为迭代器的类型(比如&Vec<T>、&[T]这类容器引用),和第一个impl的迭代器类型完全区分开,就不会出现实现冲突了。
内容的提问来源于stack exchange,提问作者user3617992
相关产品推荐
相关产品推荐

