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.