Skip to content

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

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
ultimaweapon opened this issue May 13, 2025 · 3 comments
Labels
A-iterators Area: Iterators C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@ultimaweapon
Copy link

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),
};
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label May 13, 2025
@ShE3py
Copy link
Contributor

ShE3py commented May 13, 2025

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

@rustbot rustbot added A-iterators Area: Iterators C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels May 13, 2025
@hanna-kruppe
Copy link
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>.

@zachs18
Copy link
Contributor

zachs18 commented May 14, 2025

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),
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-iterators Area: Iterators C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants