Skip to content

Default implementation on std::iter::Fuse should not requires Default on the inner iterator #140961

Closed

Description

@ultimaweapon

So it can be constructed in the following code:

let items = match std::fs::read_dir(&path) {
    Ok(v) => v.fuse(),
    Err(e) if e.kind() == ErrorKind::NotFound => Default::default(), // This line won't compile.
    Err(e) => return Err(e),
};

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on May 13, 2025
  2. ShE3py commented on May 13, 2025

    @ShE3py
    Contributor

    The problem is that Fuse::<I>::default() calls I::default(), which is free to yield some elements. Maybe we should add a Fuse::<I>::exhausted() which is guaranteed to yield no elements?

    @rustbot label +T-libs-api +C-feature-request +A-iterators -needs-triage

  3. added
    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.
    T-libs-api[DEPRECATED; DO NOT USE]
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on May 13, 2025
  4. hanna-kruppe commented on May 13, 2025

    @hanna-kruppe
    Contributor

    Oh no… that’s what the documentation of impl Default for Fuse says, but the current implementation actually constructs Fuse { iter: Default::default() } which is Fuse { iter: None } because the field is Option<I>.

  5. zachs18 commented on May 14, 2025

    @zachs18
    Contributor

    For a workaround for the original code snippet, assuming you want an empty iterator in the ErrorKind::NotFound case, there are a few things you could do.

    If you don't mind an additional heap allocation and dynamic dispatch, you could use Box<dyn Iterator>:

    let mut items: Box<dyn Iterator<Item = _>> = match std::fs::read_dir(&path) {
        Ok(v) => Box::new(v),
        Err(e) if e.kind() == ErrorKind::NotFound => Box::new(std::iter::empty()),
        Err(e) => return Err(e),
    };

    Or you could use Option::into_iter and Iterator::flatten, e.g.

    let mut items = match std::fs::read_dir(&path) {
        Ok(v) => Some(v).into_iter().flatten(),
        Err(e) if e.kind() == ErrorKind::NotFound => None.into_iter().flatten(),
        Err(e) => return Err(e),
    };

    Or you could use either::Either:

    let mut items = match std::fs::read_dir(&path) {
        Ok(v) => Either::Left(v),
        Err(e) if e.kind() == ErrorKind::NotFound => Either::Right(std::iter::empty()),
        Err(e) => return Err(e),
    };

    Or, if you are using Rust 1.79.0 or later and depending on how you use items, you could use dynamic dispatch with the new temporary value lifetime extension semantics in match:

    let items: &mut dyn Iterator<Item = _> = match std::fs::read_dir(&path) {
        Ok(v) => &mut {v}, // the braces are required, to move `v` out of the variable into a temporary whose lifetime can be extended
        Err(e) if e.kind() == ErrorKind::NotFound => &mut std::iter::empty(),
        Err(e) => return Err(e),
    };
  6. ultimaweapon commented on May 14, 2025

    @ultimaweapon
    Author

    Thanks for the idea. I don't mind using a workaround but I think it is better to be able to construct Fuse directly is better (e.g. provide additional method like @ShE3py said). Also I think changing the current behavior of Fuse::default is a breaking change since people might already depend on its current behavior.

  7. added a commit that references this issue on May 15, 2025
  8. added a commit that references this issue on Jun 23, 2025
  9. added a commit that references this issue on Jun 24, 2025
  10. added a commit that references this issue on Jul 3, 2025
  11. zachs18 commented on Jul 10, 2025

    @zachs18
    Contributor

    The issue title has been resolved, since #140985 merged so <Fuse<I> as Default>::default now calls I::default (as it was documented to), so it does require I: Default.

    The issue could be renamed to reflect the feature request for an exhausted() -> Fuse function, but generally library feature requests go through the API Change Proposal process. If you have a use-case for producing a Fuse that is already exhausted, you could maybe submit an ACP for a pub const fn exhausted() -> Self on Fuse.

  12. ultimaweapon commented on Jul 11, 2025

    @ultimaweapon
    Author

    Thanks for the information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-iteratorsArea: IteratorsC-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-libs-api[DEPRECATED; DO NOT USE]

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions