Skip to content

Add is_empty() to Iterator #90676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,33 @@ pub trait Iterator {
(0, None)
}

/// Returns whether the iterator has no items left.
///
/// The default implementation returns `true` when [`size_hint()`] returns <code>(0, [Some](0))</code>,
/// and `false` otherwise.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let a = [1, 2, 3];
/// let mut iter = a.iter();
///
/// assert!(!iter.is_empty());
///
/// for _ in iter {
/// // consume the iterator, leaving no items left
/// }
///
/// assert!(iter.is_empty());
/// ```
#[inline]
#[unstable(feaure = "iter_is_empty", reason = "recently added", issue = "0")]
fn is_empty(&self) -> bool {
(0, Some(0)) == self.size_hint()
Copy link
Member

@scottmcm scottmcm Nov 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no requirement that the size_hint be this precise (unless it's ExactSizeIterator or TrustedLen), so I don't think this implementation is ok. For example, this won't work with from_fn: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0fce510cb4a02171b289d6d3bd0e8808

This would either need to be -> Option<bool> (so it can return None if the size_hint is inconclusive), or be on a different trait (as you mention).

Copy link
Member Author

@fee1-dead fee1-dead Nov 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from_fn obviously doesn't work, but I don't see why this is bad. This method is not always correct when it returns false (return value of false does not mean the iterator has items), but it should be correct when it returns true.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not what I'd expect it to do, at least. I'd expect it, since Rust has all the type system machinery it needs to do this, to only compile when it works -- same as .len() isn't "well, here's what it might be".

Someone who wants unreliable checks can always just do .size_hint().0 > 0 or .size_hint().1 == Some(0), depending on which direction they want; I don't think convenience methods for that are appropriate.

Copy link
Member Author

@fee1-dead fee1-dead Nov 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESI is a safe trait, and to me len() is also "here's what it might be" since we can't say not reporting accurate size is UB. is_empty doesn't have to reside in another trait to me, but we could add a trait for users to require iterators to produce accurate is_empty results, like FusedIterator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESI is a safe trait, and to me len() is also "here's what it might be" since we can't say not reporting accurate size is UB.

UB is not the only kind of forbidden behavior. Safe traits can also make promises where breaking these promises is a bug that can cascade into further downstream misbehavior such as infinite loops, panics, leaks and other API contracts being broken which easily can result in data loss or miscomputations. Sure, it's not a buffer overflow or double-free but that's little consolation when your blob storage containing petabytes of critical data suddenly become inaccessible, your account balance gets miscomputed or an airliner has an intersection event with a mountain.

So from the perspective of safe code ExactSizeIterator::len is guaranteed to be correct and it is free to operate on that assumption for all T. B is correct if A is correct.

is_empty here does not meet this level of conditional correctness and would have to be weakened to the level of uselessness because it would have to explicitly mention something like

For some iterator types this function may always return false even when they are empty.

Which renders it useless as a trait method, because then people would have to reason about concrete types rather than the trait.

}

/// Consumes the iterator, counting the number of iterations and returning it.
///
/// This method will call [`next`] repeatedly until [`None`] is encountered,
Expand Down