rustiterator

How does one get an iterator to the max value element in Rust?


I want to access the element next to the maximal one in a Vec<i32>. I'm looking for something like this:

let v = vec![1, 3, 2];
let it = v.iter().max_element();
assert_eq!(Some(&2), it.next());

In C++, I would go with std::max_element and then just increase the iterator (with or without bounds checking, depending on how adventurous I feel at the moment). The Rust max only returns a reference to the element, which is not good enough for my use case.

The only solution I came up with is using enumerate to get the index of the item - but this seems manual and cumbersome when compared to the C++ way.

I would prefer something in the standard library.

This example is simplified - I actually want to attach to the highest value and then from that point loop over the whole container (possibly with cycle() or something similar).


Solution

  • C++ iterators are not the same as Rust iterators. Rust iterators are forward-only and can only be traversed once. C++ iterators can be thought of as cursors. See What are the main differences between a Rust Iterator and C++ Iterator? for more details.

    In order to accomplish your goal in the most generic way possible, you have to walk through the entire iterator to find the maximum value. Along the way, you have to duplicate the iterator each time you find a new maximum value. At the end, you can return the iterator corresponding to the point after the maximum value.

    trait MaxElement {
        type Iter;
    
        fn max_element(self) -> Self::Iter;
    }
    
    impl<I> MaxElement for I
    where
        I: Iterator + Clone,
        I::Item: PartialOrd,
    {
        type Iter = Self;
    
        fn max_element(mut self) -> Self::Iter {
            let mut max_iter = self.clone();
            let mut max_val = None;
    
            while let Some(val) = self.next() {
                if max_val.as_ref().map_or(true, |m| &val > m) {
                    max_iter = self.clone();
                    max_val = Some(val);
                }
            }
    
            max_iter
        }
    }
    
    fn main() {
        let v = vec![1, 3, 2];
        let mut it = v.iter().max_element();
        assert_eq!(Some(&2), it.next());
    }
    

    See also:

    I actually want to attach to the highest value and then from that point loop over the whole container (possibly with cycle() or something similar).

    In that case, I'd attempt to be more obvious:

    fn index_of_max(values: &[i32]) -> Option<usize> {
        values
            .iter()
            .enumerate()
            .max_by_key(|(_idx, &val)| val)
            .map(|(idx, _val)| idx)
    }
    
    fn main() {
        let v = vec![1, 3, 2];
        let idx = index_of_max(&v).unwrap_or(0);
        let (a, b) = v.split_at(idx);
        let mut it = b.iter().chain(a).skip(1);
        assert_eq!(Some(&2), it.next());
    }
    

    See also: