I decided on trying to make a trait in Rust using blanket implementation, and the test method to implement was a trait that returns the argmax over an iterator together with the element. Right now the implementation is
use num::Bounded;
trait Argmax<T> {
fn argmax(self) -> (usize, T);
}
impl<I, T> Argmax<T> for I
where
I: Iterator<Item = T>,
T: std::cmp::PartialOrd + Bounded,
{
fn argmax(self) -> (usize, T) {
self.enumerate()
.fold((0, T::min_value()), |(i_max, val_max), (i, val)| {
if val >= val_max {
(i, val)
} else {
(i_max, val_max)
}
})
}
}
Testing it with this code
fn main() {
let v = vec![1., 2., 3., 4., 2., 3.];
println!("v: {:?}", v);
let (i_max, v_max) = v.iter().copied().argmax();
println!("i_max: {}\nv_max: {}", i_max, v_max);
}
works, while
fn main() {
let v = vec![1., 2., 3., 4., 2., 3.];
println!("v: {:?}", v);
let (i_max, v_max) = v.iter().argmax();
println!("i_max: {}\nv_max: {}", i_max, v_max);
}
doesn't compile, and gives these errors:
--> src/main.rs:27:35
|
27 | let (i_max, v_max) = v.iter().argmax();
| ^^^^^^ method cannot be called on `std::slice::Iter<'_, {float}>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`<&std::slice::Iter<'_, {float}> as Iterator>::Item = _`
which is required by `&std::slice::Iter<'_, {float}>: Argmax<_>`
`&std::slice::Iter<'_, {float}>: Iterator`
which is required by `&std::slice::Iter<'_, {float}>: Argmax<_>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0599`.
I figure that the problem originates from the fact that .iter() loops over references, while .iter().copied() loops over actual values, but I still can't wrap my head around the error message and how to make it generic and working with looping over references.
EDIT: After being recommended trying to implement the above using associated types instead of generic types, and ended up with this working implementation for later reference:
trait Argmax {
type Maximum;
fn argmax(self) -> Option<(usize, Self::Maximum)>;
}
impl<I> Argmax for I
where
I: Iterator,
I::Item: std::cmp::PartialOrd,
{
type Maximum = I::Item;
fn argmax(mut self) -> Option<(usize, Self::Maximum)> {
let v0 = match self.next() {
Some(v) => v,
None => return None,
};
Some(
self.enumerate()
.fold((0, v0), |(i_max, val_max), (i, val)| {
if val > val_max {
(i + 1, val) // Add 1 as index is one off due to next() above
} else {
(i_max, val_max)
}
}),
)
}
}
This implementation also has Bounded removed as dependency, and instead checks if the iterator is empty, and if not, initializes the current maximum with the first element returned by the iterator. This implementation returns the index of the first maximum it finds.