I have something that implements std::iter::Iterator
and I want to know if there are > 0
elements. What is the standard way to do it? count() > 0
looks too expensive.
I see two candidates: any(|_| true)
and nth(0).is_some()
, but which one should I pick so a future reader can understand on sight what I'm checking here?
I would write iter.next().is_some()
.
However, you need to be aware that doing this advances the iterator.
fn main() {
let scores = [1, 2, 3];
let mut iter = scores.iter();
println!("{}", iter.next().is_some()); // true
println!("{}", iter.next().is_some()); // true
println!("{}", iter.next().is_some()); // true
println!("{}", iter.next().is_some()); // false
}
In many cases I'd use Peekable
:
fn main() {
let scores = [1, 2, 3];
let mut iter = scores.iter().peekable();
println!("{}", iter.peek().is_some()); // true
println!("{}", iter.peek().is_some()); // true
println!("{}", iter.peek().is_some()); // true
println!("{}", iter.peek().is_some()); // true
}
so a future reader can understand on sight
I'd add a method on iterator named is_empty
.
The canonical way to implement is_empty
for Iterator
is not to do so. An Iterator
is lazy, so by definition cannot know if it has any more elements without iterating.
Logically, it might seem as if it should be simple for an Iterator to know whether it has any more elements, but that can only be the case (without iterating) if its size in known. And indeed, ExactSizeIterator implements is_empty
.
To check whether an Iterator
is empty, you must attempt to iterate and check if you receive None
, however (as mentioned by @Shepmaster), you can only do so without advancing the Iterator
when you have a Peekable
Iterator
. You can peek()
at the next element and check if it is_none()
:
let mut iterator = vec![1,2,3].into_iter().peekable();
println!("is_empty: {}", iterator.peek().is_none());
This becomes more obvious when considering that an to implement Iterator
, one only needs to provide a next
function that returns either Some
or None
to indicate whether further elements exist. We could provide an implementation of next
that randomly decides whether the next element exists, which makes it clear that we can't know without executing next
whether our iterator is empty:
struct RandomThings {}
impl Iterator for RandomThings {
type Item = bool;
fn next(&mut self) -> Option<bool> {
let has_next = rand::random::<bool>();
if has_next {Some(true)} else {None}
}
}
So creating a Peekable
Iterator and calling .peek().is_none()
is actually very explicit, and should be easily understood by future readers. If you are only dealing with iterators of known size, then you can constrain your type further to ExactSizeIterator
and use is_empty
. If you were to add is_empty
for an ordinary Iterator
you would only be hiding the fact that its next
function must be called to determine whether it is empty.