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
.