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? # for a free GitHub account to open an issue and contact its maintainers and the community.

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

Already on GitHub? # 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
# for free to join this conversation on GitHub. Already have an account? # 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.