6

Rust has two main traits for iteration: the standard Iterator for things that can be traversed in order, and DoubleEndedIterator for things that can additionally be iterated from the back.

This makes sense to me. A lot of useful methods are defined on Iterator, like map, filter, and whatnot. And then things that operate from the "back", so to speak, like rfind and rfold are defined on DoubleEndedIterator since such operations only make sense on such iterators.

But then we've got Iterator::rev

fn rev(self) -> Rev<Self>
where
    Self: Sized + DoubleEndedIterator,
{
    Rev::new(self)
}

rev is a function defined on Iterator but with an additional constraint on Self that it be DoubleEndedIterator as well.

What is the practical benefit to having this function defined on Iterator? We can only call it on DoubleEndedIterator implementors, and DoubleEndedIterator extends Iterator so we'll never end up in a situation where something implements the former and not the latter. So why is rev not defined on DoubleEndedIterator like rfind and rfold are?

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • it's always better to try to answer "why not" – Stargateur Apr 14 '22 at 20:23
  • For what it's worth, here is [the relevant commit](https://github.com/rust-lang/rust/commit/de1446680cab4102cef9d0e133f1b835574926eb). The method used to be on a trait called `DoubleEndedIteratorExt` that required `DoubleEndedIterator + Sized`. The `DoubleEndedIteratorExt` trait was then merged into `Iterator`. I can't see why it wasn't merged into `DoubleEndedIterator` instead. – Sven Marnach Apr 14 '22 at 20:47
  • If you need to call it statically or pass it as the function per se, it's a bit nicer to write `Iterator::rev` than `DoubleEndedIterator::rev`. – BallpointBen Apr 14 '22 at 20:47
  • @SvenMarnach nevermind merge it would force every implementation of Iterator to implement `Rev` aka be a doubleendediterator and OP didn't ask for why it's not merged – Stargateur Apr 14 '22 at 20:50
  • 1
    There are a couple more methods that require `DoubleEndedIterator` on the `Iterator` triait, `partition_in_place()` and `rposition()`. Looking at the methods that are on `DoubleEndedIterator` proper, it looks like they are the ones that are useful to specialize for specific bidirectional iterators, while the ones on the `Iterator` trait generally are not useful to specialize. Whether this is the reason they are split up this way, I don't know. I also don't see a forcing reason why it should be this way, since methods on either trait could be specialized. – Sven Marnach Apr 14 '22 at 21:02

1 Answers1

4

This allow to:

  • see all features (doc) of Iterator in one place.
  • use rev() on an Iterator without need to use DoubleEndedIterator, the prelude include it so it's not a problem actually. I would so add this reduce cognitive load by letting the user ignore DoubleEndedIterator exist.
Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • 3
    I'm not sure this really explains why `rev()` is treated differently than the methods that are directly defined on the `DoubleEndedIterator` trait. – Sven Marnach Apr 14 '22 at 20:50
  • @SvenMarnach I don't even see why these other method exist the all can be replace by `rev().foo()` like `rfold()` == `.rev().fold()` so there are not needed on Iterator directly, I guess there exist for optimisation purpose. – Stargateur Apr 15 '22 at 03:52
  • @SvenMarnach All but `rfind()` are methods the iterator can, or should, implement to make the various iteration options faster. I don't know why `rfind()` is also there. – Chayim Friedman Apr 15 '22 at 06:24
  • 1
    The first argument is not enough since `DoubleEndedIterator` is [in the prelude](https://doc.rust-lang.org/stable/std/prelude/v1/index.html). – Chayim Friedman Apr 15 '22 at 06:26
  • 1
    @ChayimFriedman wow... strange.. nice catch... this reduce the list to one..... :p it's a wiki answer to feel free to change it – Stargateur Apr 15 '22 at 06:28