Home > Enterprise >  How can I tell rust two traits have the same type?
How can I tell rust two traits have the same type?

Time:11-01

I'm trying implement C adjacent_difference algo for rust iterator. std::adjacent_different by default uses the current value in a container subtracts its previous value, something like, if we have [5, 7, 16], then after adjacent_difference, we get [5, 7-5, 16-7]

I try to do it in rust as an iterator extension, but found out that Iterator::Item - Iterator::Item is std::ops::Sub::Output, to make the Iterator::next return type happy, I have to do some weird things, like Some(iter_item_value - <I as Iterator>::Item::default()) to get the correct type godbolt

struct AdjacentDifference<I>
where I: Iterator
{
    prev: Option<I::Item>,
    it: I,
}

impl<I> Iterator for AdjacentDifference<I>
where I: Iterator, <I as Iterator>::Item: std::ops::Sub   Default   Copy,
{
    type Item = <<I as Iterator>::Item as std::ops::Sub>::Output;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.it.next(), self.prev) {
            (None, _) => None,
            (Some(i), Some(p)) => {
                let r = i - p;
                self.prev = Some(i);
                Some(r)
            }
            (Some(i), None) => {
                self.prev = Some(i);
                Some(i - <I as Iterator>::Item::default()) // <<- really?
            }
        }
    }
}

trait AdjacentDifferenceExt: Iterator where Self: Sized,
{
    fn adjacent_difference(self) -> AdjacentDifference<Self>
    where <Self as Iterator>::Item: Copy,
    {
        AdjacentDifference {
            prev: None,
            it: self,
        }
    }
}

impl<I> AdjacentDifferenceExt for I where I: Iterator {}

It works, but I don't like this code very much, did I do this totally wrong? are there any better ways to achieve this?

CodePudding user response:

There are multiple ways you can tackle this.

For example, one option is just to specify the subtraction has the same type as the element itself:

impl<I> Iterator for AdjacentDifference<I>
where
    I: Iterator,
    I::Item: std::ops::Sub<Output = I::Item>   Copy,
{
    type Item = <<I as Iterator>::Item as std::ops::Sub>::Output;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.it.next(), self.prev) {
            (None, _) => None,
            (Some(i), Some(p)) => {
                let r = i - p;
                self.prev = Some(i);
                Some(r)
            }
            (Some(i), None) => {
                self.prev = Some(i);
                Some(i)
            }
        }
    }
}

This restricts the type you can use (for example, you won't be able to use Instant - Instant = Duration, but for most types this will be OK.

Another option is to specify that the iterator element type can be converted into the subtraction type via From:

impl<I> Iterator for AdjacentDifference<I>
where
    I: Iterator,
    I::Item: std::ops::Sub   Copy,
    <I::Item as std::ops::Sub>::Output: From<I::Item>,
{
    type Item = <<I as Iterator>::Item as std::ops::Sub>::Output;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.it.next(), self.prev) {
            (None, _) => None,
            (Some(i), Some(p)) => {
                let r = i - p;
                self.prev = Some(i);
                Some(r)
            }
            (Some(i), None) => {
                self.prev = Some(i);
                Some(i.into())
            }
        }
    }
}

This will implicitly work for the cases where the subtraction type is the element type thank to the blanket impl<T> From<T> for T.

  • Related