Chaining an `Option<Iterator>` (`unwrap_or` with traits leads to conflicting actual types)

I have a function where I want to read files from a directory and then optionally concat the files from another one, if it exists. The tests/snapshots directory is guaranteed to exist, but tests/snapshots/hidden might not be created (because it might contain sensitive files, it's hidden from version control, and thus is not created on CI). If it doesn't exist, I don't want to panic, though, but simply ignore it.

(get_dir simply constructs the appropriate absolute path from the relative one.)

let snapshots = fs::read_dir(get_dir("tests/snapshots")).unwrap();
let hidden_snapshots_result = fs::read_dir(get_dir("tests/snapshots/hidden"));
let snapshots = snapshots.chain(hidden);

My intuition tells me to do hidden_snapshots.unwrap_or(std::iter::empty()). But of course, the concrete types differ and so compilation fails.

error[E0308]: mismatched types
  --> tests/snapshots.rs:14:93
   |
14 |     let hidden_snapshots_result = fs::read_dir(get_dir("tests/snapshots/hidden")).unwrap_or(std::iter::empty());
   |                                                                                             ^^^^^^^^^^^^^^^^^^ expected struct `std::fs::ReadDir`, found struct `std::iter::Empty`
   |
   = note: expected type `std::fs::ReadDir`
              found type `std::iter::Empty<_>`

I can't figure out, how to make this work without resorting to boxing or collecting, which is less performant and seems unnecessary. I get the feeling that I'm taking the wrong approach.

How would you go about this problem?

You could use the either crate for this, Either implements Iterator if both sides implement Iterator.

let hidden_snapshots_result = fs::read_dir(Either::Left(get_dir("tests/snapshots/hidden"))).unwrap_or(Either::Right(std::iter::empty()));
1 Like

Another option is fs::read_dir(...).into_iter().flatten() -- iterate the Result (a single item if Ok, or nothing for Err) and flatten that item as an iterator in itself.

2 Likes

I think I prefer the flatten() approach, because introducing another types feels more complicated. The flattening looks so simple, I don't understand how I didn't figure it out by myself. :smile:

Thanks to both of you for your answers!