1
votes

It seems to me that whether an option is the right type to return should be up to the implementor.

I notice that it goes away when I try to filter or using other collection methods on the items. Is this simply a replacement for has_next? Won't it have potential performance/memory implications?

1
What should [].iter().next() do?trent ᶠᵒʳᵐᵉʳˡʸ ᶜˡ
"and won't it have potential performance/memory-implications?" no more that any < size or != NULL or similar.Stargateur
@trentcl I guess it's a priority in Rust to avoid panic on an exhausted iterators. Other languages use has_next and then panics on next. It just feels like a big overhead on a quite low-level construct.thoredge
@Stargateur If you create an iterator over bytes (u8) you will have created as many Somes has you have bytes.thoredge
@thoredge no, it's an additional 8 bytes at maximum (and often even 0) for one entry at a time; moving to the next Item drops the previous one.ljedrz

1 Answers

7
votes

Because it needs some way to communicate to the caller that there's nothing left to output.

fn main() {
    let mut it = vec![1, 2, 3].into_iter();
    assert_eq!(it.next(), Some(1));
    assert_eq!(it.next(), Some(2));
    assert_eq!(it.next(), Some(3));
    assert_eq!(it.next(), None); // End of iterator.
}

As for a hypothetical has_next, that can complicate some iterator designs because it requires the iterator to know whether there is another element. This might require the iterator to compute the next element, then store it somewhere. It's also possible to forget to call has_next, or call it but ignore the result.

With next returning an Option, none of this is an issue; an iterator can compute the next item and return it whilst making it impossible for a caller to forget to ensure the returned value actually has something in it.

One thing this does not let you do is "peek" at the iterator to see if there's something more and then change logic based on that answer, without actually consuming the next item. However, that's what the peekable combinator is for, which gives you what amounts to a traditional has_next: peek().is_some().

On your concerns about performance: I've never seen anything to suggest there is any penalty. Anything using an iterator correctly has to check to see if it's reached the end. As for space, a Rust iterator doesn't need to cache the next item, so they're likely to be the same size or smaller than an iterator for a language that uses has_next.

Finally, as noted in comments, Option is not heap allocated. A None is equivalent to a false followed by some uninitialised space (since there's nothing in it), and a Some(v) is equivalent to a true followed by v.