Skip to content
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

Add is_empty function to ExactSizeIterator #35428

Open
frewsxcv opened this issue Aug 6, 2016 · 73 comments
Open

Add is_empty function to ExactSizeIterator #35428

frewsxcv opened this issue Aug 6, 2016 · 73 comments
Labels
A-iterators Area: Iterators B-unstable Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@frewsxcv
Copy link
Member

frewsxcv commented Aug 6, 2016

Tracking issue for functionality introduced in #34357

@frewsxcv
Copy link
Member Author

frewsxcv commented Aug 6, 2016

Added reference to this issue in #35429

@apasel422 apasel422 added the B-unstable Implemented in the nightly compiler and unstable. label Aug 6, 2016
jntrnr pushed a commit to jntrnr/rust that referenced this issue Aug 7, 2016
…l422

Indicate tracking issue for `exact_size_is_empty` unstability.

rust-lang#35428
@alexcrichton alexcrichton added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Aug 7, 2016
jntrnr pushed a commit to jntrnr/rust that referenced this issue Aug 7, 2016
…l422

Indicate tracking issue for `exact_size_is_empty` unstability.

rust-lang#35428
jntrnr pushed a commit to jntrnr/rust that referenced this issue Aug 7, 2016
…l422

Indicate tracking issue for `exact_size_is_empty` unstability.

rust-lang#35428
@SimonSapin
Copy link
Contributor

SimonSapin commented Oct 17, 2016

I wanted to use this today until I realized that it’s unstable. (I could use .len() == 0 in the mean time.) Seems straightforward enough. FCP to stabilize?

@alexcrichton
Copy link
Member

alexcrichton commented Nov 1, 2016

@rfcbot fcp merge

Seems good to have consistency!

@rfcbot
Copy link

rfcbot commented Nov 1, 2016

Team member @alexcrichton has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@bluss
Copy link
Member

bluss commented Nov 4, 2016

@SimonSapin I'm curious, when do you use ExactSizeIterator and have the opportunity to use this?

@SimonSapin
Copy link
Contributor

SimonSapin commented Nov 5, 2016

When using a slice or vec iterator as input for a parser, .is_empty() is a way to tell "have I reach the end of the input yet?" https://github.com/servo/html5ever/blob/b5c4552fab/macros/match_token.rs#L175

@bluss
Copy link
Member

bluss commented Nov 5, 2016

Aha, that explains it for me: using a specific iterator type, not using ESI generically. This method is good, reinforces existing conventions.

@rfcbot
Copy link

rfcbot commented Nov 12, 2016

🔔 This is now entering its final comment period, as per the review above. 🔔

psst @alexcrichton, I wasn't able to add the final-comment-period label, please do so.

@alexcrichton alexcrichton added the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Nov 12, 2016
@rfcbot
Copy link

rfcbot commented Nov 22, 2016

The final comment period is now complete.

@bluss
Copy link
Member

bluss commented Nov 22, 2016

used in PR #37943

@bluss
Copy link
Member

bluss commented Nov 23, 2016

Here's a thought: Some iterators can implement a good is_empty() even if they can't do ESI or .len(). One example is .chars(). Does it matter?

@aturon
Copy link
Member

aturon commented Dec 15, 2016

@bluss Sorry I missed your comment when prepping the stabilization PR.

My feeling is that these iterators can/should provide an inherent is_empty method if we think that's useful. The addition of the method to ExactSizeIterator is more about convenience than generic programming, IMO.

FWIW, it's also a longstanding desire on the lang side to have an API evolution path for splitting a trait into supertraits without breaking existing code. It's not guaranteed to happen, but that would allow us to split out is_empty into its own trait if we wanted to program generically with it in std in the future. (Of course, we can always add a separate trait with a blanket impl, worst case.)

@aturon
Copy link
Member

aturon commented Dec 15, 2016

I'm removing is_empty from the current stabilization PR, to give more time for discussion here.

@bluss
Copy link
Member

bluss commented Dec 15, 2016

  • I think emptiness should be a trait, so that it can pass through adapters
  • Iterators as lazy sequences have a different relationship to emptiness than collections, and the common chars() can already tell emptiness but not length; it's a common case in for example parsers (maybe not the most common, since byte-level parsing seems to be the most popular).

@aturon
Copy link
Member

aturon commented Dec 15, 2016

@bluss

Both points make sense. However, without further language improvements, it will be tricky to meet those goals and the original ergonomic goal at the same time.

Ideally, ExactSizeIterator would be a subtrait of IsEmpty and provide a default implementation of the is_empty method. To make this work, we'll need both specialization and the ability to implicitly impl supertraits when impl'ing a subtrait (an idea we've been kicking around on the lang team precisely to allow for this kind of API evolution).

Alternatively, we could add the IsEmpty trait to the prelude, along with a blanket impl from ExactSizeIterator. That comes with its own backwards-compatibility risks, though.

@bluss
Copy link
Member

bluss commented Dec 15, 2016

don't we have a backdoor for adding methods like this? trait Iterator has the method .rposition() where Self: DoubleEndedIterator

@aturon
Copy link
Member

aturon commented Dec 15, 2016

@bluss I may be missing something, but I don't see how that helps here. To clarify, the competing goals I see are:

  • A separate IsEmpty trait that can be properly forwarded along adapters, and programmed over generically.
  • Ergonomic access to is_empty for any ExactSizeIterator.

I don't offhand see how the trick you mention helps here; maybe you can spell it out?

It's worth noting that we could provide an is_empty method both in ExactSizeIterator and in a separate IsEmpty trait, with a blanket impl as well. But of course if you have both traits in scope at the same time, you'll have to explicitly disambiguate.

@bluss
Copy link
Member

bluss commented Dec 15, 2016

It's easier said than done, apparently. trait Iterator can get a new method, something like:

fn is_empty(&self) -> bool
    where Self: IsEmptyIterator;

which fixes the issue with having the method in the prelude.

But to arrange the right blanket implementations for IsEmptyIterator does not seem to be possible, even with the specialization features that are in nightly now.

@alexcrichton alexcrichton removed the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Dec 19, 2016
@SimonSapin
Copy link
Contributor

SimonSapin commented Mar 9, 2017

Did the IsEmpty trait preempt the stabilization FCP?

@aturon
Copy link
Member

aturon commented Mar 13, 2017

@SimonSapin Yes, this ended up getting pulled from the stabilization PR and has been sitting idle since then. Needs someone to drive it to a conclusion.

@scottmcm
Copy link
Member

scottmcm commented Jul 8, 2019

Over in #61366 (comment), @SimonSapin asked for other possibilities, so here's something I tried out today: https://github.com/rust-lang/rust/compare/master...scottmcm:is_empty-alternative?expand=1

It moves is_empty to a new trait that depends on a size_hint being lower > 0 || upper == Some(0), which it implements automatically for ExactSizeIterator and TrustedLen. So one path forward here could be to stabilize the method but not the trait, which would allow people to start using the method (I put the trait in the prelude because its previous position on ESI was there). Then we could stabilize the trait once specialization stabilizes and other implementations wouldn't be blocked by coherence.

Thoughts?

@nox
Copy link
Contributor

nox commented Jul 23, 2019

Note that users can't make their own inherent is_empty method on their own types if they also want to implement ExactSizeIterator on them, any use triggers an "use of unstable library feature 'exact_size_is_empty'" error.

@SimonSapin
Copy link
Contributor

SimonSapin commented Jul 23, 2019

I can’t reproduce this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cec84e30d1eedd6e5fa6933b4e1f8631 Did you mean something else?

@nox
Copy link
Contributor

nox commented Jul 23, 2019

Ouch, it fails when the receiver of the call is a &mut.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ed565c343e2fc196ea1e3dc4f2e54ad1

@nox
Copy link
Contributor

nox commented Jul 23, 2019

I assume it ends up calling <&mut Oops as ExactSizeIterator>::is_empty rather than Oops::is_empty or <Oops as ExactSizeIterator>::is_empty.

@SimonSapin
Copy link
Contributor

SimonSapin commented Jul 23, 2019

Ah that’s a good point, presumably through impl<I: Iterator + ?Sized> Iterator for &mut I.

@nox
Copy link
Contributor

nox commented Jul 30, 2019

@scottmcm

Thoughts?

Can you imagine a scenario where you would want to have a trait bound on KnowsEmptyIterator but not ExactSizeIterator, and where you wouldn't be able to just call Iterator::next until a None is returned?

@gin-ahirsch
Copy link

gin-ahirsch commented Jan 16, 2020

@nox

Can you imagine a scenario where you would want to have a trait bound on KnowsEmptyIterator but not ExactSizeIterator, and where you wouldn't be able to just call Iterator::next until a None is returned?

In general, when an iterator is processed, but the last element needs to be handled differently. You can always get around this by buffering one element, e.g. using a Peekable<Iterator> instead of a plain Iterator, where is_empty() == peek().is_none().

In an application of mine for instance, I have a parser that scans log-files for various events and groups them into "runs" (i.e. events delimited by a start- and end-event). It is capable of detecting some instances of missing start- or end-events.
Usually logs are cut off at the start and the end, so there will virtually always be an initial missing start and a final missing end event. I have decided to make the parser emit a "complete" event chain, so it will output those "missing start/end" events for the first/last run.
I filter them out when e.g. writing the detected events to a file though, for which some tools exist to visualize the events. Since the user knows that the first and last runs are incomplete, having these is just noise.
To filter the last "missing stop"-event I need to know if I am processing the last run. I have implemented the function by accepting an Iterator<&Event>, so currently I wrap it in a Peekable as mentioned above. I don't really need to peek, it is just to check if it is the last run.
The fact that the parser emits only "complete" event chains is currently not used anywhere, so it's essentially an arbitrary decision which triggers the use-case, but I think it stands as an example. I'll concede that it may be too specific of a use-case, also because I have a functioning workaround.

@jonas-schievink jonas-schievink added the A-iterators Area: Iterators label Mar 29, 2020
@KodrAus KodrAus added the Libs-Tracked Libs issues that are tracked on the team's project board. label Jul 29, 2020
@behnam
Copy link
Contributor

behnam commented Sep 27, 2020

Hi there,

Looks like it hasn't been mentioned on this issue (afaict), so sharing a tiny problem I noticed with the current non-stabilized is_empty() from ExactSizeIterator, in case it needs any action before stabilization.

For a simple type, such as:

pub type SomeRange = Range<usize>;

When std::iter::ExactSizeIterator is in scope, the following compile error happens when is_empty() used on a SomeRange value:

error[E0034]: multiple applicable items in scope
   |
   |         assert!(!some_range.is_empty());
   |                  ----^^^^^^^^--
   |                  |   |
   |                  |   multiple `is_empty` found
   |                  help: disambiguate the associated function for candidate #2: `std::iter::ExactSizeIterator::is_empty(&some_range)`
   |
   = note: candidate #1 is defined in an impl for the type `std::ops::Range<Idx>`
   = note: candidate #2 is defined in an impl of the trait `std::iter::ExactSizeIterator` for the type `std::ops::Range<usize>`

@scottmcm
Copy link
Member

scottmcm commented Sep 28, 2020

Good news: Range::is_empty() is stable in beta (#75132), so this no longer happens there, and will be fixed in stable on Oct 8th with 1.47.

o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `impl ExactSizeIterator` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `impl ExactSizeIterator` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `impl ExactSizeIterator` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `Iter<'_, Error>` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `Iter<'_, Error>` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `Iter<'_, Error>` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
o0Ignition0o added a commit to apollographql/apollo-rs that referenced this issue Nov 24, 2021
fixes #139

ast.errors() now return an `Iter<'_, Error>` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
lrlna pushed a commit to apollographql/apollo-rs that referenced this issue Nov 29, 2021
fixes #139

ast.errors() now return an `Iter<'_, Error>` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
@Walther
Copy link

Walther commented Mar 21, 2022

Hello hi 👋 Kindest little boop & status check - what is the status of this? The docs seem to still say this is nightly-only, experimental https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html#method.is_empty

@scottmcm
Copy link
Member

scottmcm commented Mar 21, 2022

@Walther I think the same "people want this on non-ExactSizedIterators" contention still applies, and thus I expect it to stay unstable until someone has a solution for that.

For now, .len() == 0 is often just as fast, so may suffice.

@clarfonthey
Copy link
Contributor

clarfonthey commented Mar 21, 2022

An alternative for the not fast case would be to just use peekable, but that does use up space and most people would prefer it to be usable without taking up extra space.

Maybe a version like FusedIterator that makes Peekable fast might help?

EDIT: I actually really like this and might offer up a PR.

@clarfonthey
Copy link
Contributor

clarfonthey commented Mar 22, 2022

So, I did the thing and offered up a PeekableIterator trait in #95195 as a potential path to an alternative. Feel free to put your thoughts there and whether you think this is a reasonable alternative or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-iterators Area: Iterators B-unstable Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.