Skip to content

Change core::iter::Fuse's Default impl to do what its docs say it does #140985

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

zachs18
Copy link
Contributor

@zachs18 zachs18 commented May 14, 2025

The docs on impl<I: Default> Default for core::iter::Fuse<I> say (as the I: Default bound implies) that Fuse::<I>::default "Creates a Fuse iterator from the default value of I". However, the implementation creates a Fuse with Fuse { iter: Default::default() }, and since the iter field is an Option<I>, this is actually Fuse { iter: None }, not Fuse { iter: Some(I::default()) }, so Fuse::<I>::default() always returns an empty iterator, even if I::default() would not be empty.

This PR changes Fuse's Default implementation to match the documentation. This will be a behavior change for anyone currently using Fuse::<I>::default() where I::default() is not an empty iterator1, as Fuse::<I>::default() will now also not be an empty iterator.

(Alternately, the docs could be updated to reflect what the current implementation actually does, i.e. returns an always-exhausted iterator that never yields any items (even if I::default() would have yielded items). With this option, the I: Default bound could also be removed to reflect that no I is ever created.)

Current behavior example (maybe an example like this should be added to the docs either way?)

This PR changes publicly observable behavior, so I think requires at least a T-libs-api FCP?

r? libs-api

cc #140961

impl<I: Default> Default for Fuse<I> was added in 1.70.0 (#99929), and it's docs and behavior do not appear to have changed since (Fuse's iter field has been an Option since before the impl was added).

Footnotes

  1. IIUC it is a "de facto" guideline for the stdlib that an iterator type's default() should be empty (and for iterators where that would not make sense, they should not implement Default): cc https://github.com/rust-lang/libs-team/issues/77#issuecomment-1194681709 , so for stdlib iterators, I don't think this would change anything. However, if a user has a custom Iterator type I, and they are using Fuse<I>, and they call Fuse::<I>::default(), this may change the behavior of their code.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 14, 2025
@zachs18 zachs18 changed the title Change core::iter::Fuse's Default impl to do what it's docs say it does Change core::iter::Fuse's Default impl to do what its docs say it does May 14, 2025
@hanna-kruppe
Copy link
Contributor

Either way (fix the impl or fix the docs) this should probably gain a test since it’s extremely non-obvious from the code alone.

@scottmcm
Copy link
Member

And maybe change the code to either Option::default() just None or to I::default() to help emphasize, too.

@tgross35
Copy link
Contributor

I doubt this will have much of a noticeable impact but we may as well try to measure it.

@bors try

bors added a commit to rust-lang-ci/rust that referenced this pull request May 15, 2025
Change `core::iter::Fuse`'s `Default` impl to do what its docs say it does

The [docs on `impl<I: Default> Default for core::iter::Fuse<I>`](https://doc.rust-lang.org/nightly/std/iter/struct.Fuse.html#impl-Default-for-Fuse%3CI%3E) say (as the `I: Default` bound implies) that `Fuse::<I>::default` "Creates a `Fuse` iterator from the default value of `I`". However, the implementation creates a `Fuse` with `Fuse { iter: Default::default() }`, and since the `iter` field is an `Option<I>`, this is actually `Fuse { iter: None }`, not `Fuse { iter: Some(I::default()) }`, so `Fuse::<I>::default()` always returns an empty iterator, even if `I::default()` would not be empty.

This PR changes `Fuse`'s `Default` implementation to match the documentation. This will be a behavior change for anyone currently using `Fuse::<I>::default()` where `I::default()` is not an empty iterator[^1], as `Fuse::<I>::default()` will now also not be an empty iterator.

(Alternately, the docs could be updated to reflect what the current implementation actually does, i.e. returns an always-exhausted iterator that never yields any items (even if `I::default()` would have yielded items). With this option, the `I: Default` bound could also be removed to reflect that no `I` is ever created.)

[Current behavior example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=a1e0adc4badca3dc11bfb70a99213249) (maybe an example like this should be added to the docs either way?)

This PR changes publicly observable behavior, so I think requires at least a T-libs-api FCP?

r? libs-api

cc rust-lang#140961

`impl<I: Default> Default for Fuse<I>` was added in 1.70.0 (rust-lang#99929), and it's docs and behavior do not appear to have changed since (`Fuse`'s `iter` field has been an `Option` since before the impl was added).

[^1]: IIUC it is a "de facto" guideline for the stdlib that an iterator type's `default()` should be empty (and for iterators where that would not make sense, they should not implement `Default`): cc rust-lang/libs-team#77 (comment) , so for stdlib iterators, I don't think this would change anything. However, if a user has a custom `Iterator` type `I`, *and* they are using `Fuse<I>`, *and* they call `Fuse::<I>::default()`, this may change the behavior of their code.
@bors
Copy link
Collaborator

bors commented May 15, 2025

⌛ Trying commit fe06fc3 with merge 17092ad...

@bors
Copy link
Collaborator

bors commented May 15, 2025

☀️ Try build successful - checks-actions
Build commit: 17092ad (17092ad00ed8fe1e7f81a7e38238ff70779034b1)

@tgross35

This comment was marked as outdated.

@craterbot

This comment was marked as outdated.

@tgross35
Copy link
Contributor

@craterbot run mode=build-and-test

@craterbot
Copy link
Collaborator

👌 Experiment pr-140985 created and queued.
🤖 Automatically detected try build 17092ad
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 15, 2025
@craterbot
Copy link
Collaborator

🚧 Experiment pr-140985 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-140985 is completed!
📊 208 regressed and 149 fixed (631768 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the denylist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels May 17, 2025
@zachs18
Copy link
Contributor Author

zachs18 commented May 28, 2025

I've looked at the crates.io regressions:

Definitely spurious

Some form of "no space left on device":

  • aeiou-macros
  • april
  • argsyn
  • art_dice
  • async-peek
  • badmf
  • mu_pi
  • puzz-http
  • rstats
  • rusty_audio
  • safe-regex
  • simple-cache-rs
  • standard_card
  • timely-master
  • wallpaper_rs

Some form of "failed to create temporary directory":

  • ambiq-apollo3p-pac
  • async-pidfd-next
  • banka
  • simpl_cache
  • storage-path-generator

"Error building SSL dependencies": artcode

Other OS error ("Resource temporarily unavailable" when opening a file?): cannyls

I think are spurious:

  • due to measuring runtimes or similar: async-retry, dust_dds, ratelimit, shared-resource-pool-builder, simple-clock, slow_function_warning, stringsort, tokio-context
  • due to using env vars from multiple tests (and thus multiple threads): fuel-streams-nats, should-color
  • due to relying on hashmap iteration order: axsys-noun, serde-command-opts
  • due to relying on non-hashmap randomness: rs-tool

Otherwise unreliable/incorrect tests: rayoff, samus, sigalign-core (see linked PRs/issue)

I haven't figured out yet: gouth, rs-store, rust-netrc, sigio, sitrep, there. None seem to use Fuse directly at least (rg -iw fuse (case-insensitive, word-based search) on their sources yields no results).

@tgross35
Copy link
Contributor

Thanks for the analysis. I think we can hand this off to libs-api.

@rustbot label +I-libs-api-nominated +T-libs-api -T-libs

@rustbot rustbot added I-libs-api-nominated Nominated for discussion during a libs-api team meeting. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 28, 2025
@joshtriplett
Copy link
Member

This makes sense, and it sounds like it doesn't have any issues in crater, so:

@rfcbot merge

@rfcbot
Copy link
Collaborator

rfcbot commented Jun 3, 2025

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

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jun 3, 2025
@Amanieu Amanieu removed the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Jun 3, 2025
@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Jun 3, 2025
@rfcbot
Copy link
Collaborator

rfcbot commented Jun 3, 2025

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

@tgross35
Copy link
Contributor

tgross35 commented Jun 3, 2025

@zachs18 could you apply the suggestions from @scottmcm and @hanna-kruppe above? We can then merge this after the FCP completes.

@zachs18 zachs18 force-pushed the fuse-default-some branch from fe06fc3 to 0b7d68a Compare June 3, 2025 18:08
@rust-log-analyzer

This comment has been minimized.

…oes.

Add a doctest with a non-empty-by-default iterator.
@zachs18 zachs18 force-pushed the fuse-default-some branch from 0b7d68a to 9e8af12 Compare June 3, 2025 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.