rust 为Iterator在values或ref上实现trait

qgelzfjb  于 2023-11-19  发布在  其他
关注(0)|答案(2)|浏览(121)

我正在尝试实现一个能无缝地为Iterator和Iterator<&u32>工作的trait,虽然我确实找到了一个解决方案,但我不确定这是最好的一个,甚至是一个好的解决方案。我已经包括了我的前两个非工作解决方案以供参考。

问题

为u32上的迭代器实现以下trait

trait First {
    fn first(self) -> Option<u32>;
}

字符串

非工作方案

第一个实现:

impl<I> First for I
where I: Iterator<Item = u32> {
    fn first(mut self) -> Option<u32> {
        self.next()
    }
}


将适用于vec![1, 2, 3].into_iter().first()(迭代器),但不适用于vec![1, 2, 3].iter().first(),这将产生以下错误:

error[E0599]: the method `first` exists for struct `Iter<'_, {integer}>`, but its trait bounds were not satisfied


使用该第二实现:

impl<'a, I> First for I
where I: Iterator<Item = &'a u32> {
    fn first(mut self) -> Option<u32> {
        Some(*self.next()?)
    }
}


这次在into_iter()变体上将失败,并出现类似的错误:

error[E0599]: the method `first` exists for struct `IntoIter<{integer}>`, but its trait bounds were not satisfied


请注意,在这两种情况下,方法都被认为已经存在,尽管trait边界不满足。这意味着使用两种定义来获得整体工作解决方案是不可能的。

工作液

这是可行的:

impl<T, I> First for I
where
    I: Iterator<Item = T>,
    T: Borrow<u32>,
{
    fn first(mut self) -> Option<u32> {
        Some(*self.next()?.borrow())
    }
}


虽然这是可行的,但我不确定Borrow trait的语义(我所理解的)如何证明使用它是正确的。我也不确定这最终是如何编译的,但在into_iter()的情况下,引用和拥有整数似乎有点无意义,只有尊重和复制它。
我是不是缺少了什么让这一切顺利进行的东西,还是连尝试这样的东西都是个错误(对于Copy来说这不是什么大问题,但是对于Clone类型,即使类型已经被拥有,调用.clone()的trait也是一种浪费或资源)?还有,有没有什么方法可以让错误消息更明确:显式声明迭代器期望的是一个拥有的值,而不是一个引用,而不是关于未满足的trait的消息?

brqmpdu1

brqmpdu11#

这两个实现实际上并不冲突,因为没有类型可以同时用Item = u32Item = &u32实现Iterator。这里编译器出错了。这是issue #20400
使用Borrow是一种选择。如果您不能使用它,例如因为不同的Item类型需要不同的行为,或者因为您希望支持任何引用级别,(&&&&&u32),你可以使用一个不方便但有效的解决方法。这个想法是将impl的责任传递给关联的类型。编译器不能确认impl Iterator<Item = u32>impl Iterator<Item = &u32>是不同的类型,但它可以确认u32&u32是:

trait First {
    fn first(self) -> Option<u32>;
}

trait IteratorItem {
    fn first(iter: impl Iterator<Item = Self>) -> Option<u32>;
}

impl IteratorItem for u32 {
    fn first(mut iter: impl Iterator<Item = Self>) -> Option<u32> {
        iter.next()
    }
}

impl IteratorItem for &'_ u32 {
    fn first(mut iter: impl Iterator<Item = Self>) -> Option<u32> {
        iter.next().copied()
    }
}

impl<I> First for I
where
    I: Iterator,
    I::Item: IteratorItem,
{
    fn first(self) -> Option<u32> {
        I::Item::first(self)
    }
}

字符串

nhn9ugyo

nhn9ugyo2#

实现约束T: Borrow<u32>可能是你想要的,这是trait设计的用例之一。
我也不确定这最终是如何编译的,但在into_iter()的情况下,引用并拥有整数似乎有点毫无意义,只是为了尊重和复制它。
当编译器示例化T=&u32的版本时,它几乎肯定会内联borrow()调用,并完全消除间接性。
或者,也许你实际上并不需要一个u32,也许你只是需要能够对这些值做一些特定的事情。如果你正在做加法,你可以为一些U绑定T: std::ops::Add<U>。这不清楚是否是一个合适的解决方案,因为你的问题缺乏你更高层次目标的细节。

相关问题