Returning different kinds of iterators in flat_map

In the following snippet of code, Rust complains that the return types do not match.

let line = String::new();    // assume string has content
let line: String = line.bytes()
    .flat_map(|b| {
        if b > 0x7F {
            format!("M-{}", char::from(b - 0x7F)).bytes()
        }
        else {
            [b].into_iter()    // error happens here
        }
    })
    .map(|b| { char::from(b) })
    .collect();

The error is the following:

if and else have incompatible types
expected type std::str::Bytes<'_>
found struct std::slice::Iter<'_, u8>

How can this be avoided?

You can use either, wrapping each variant into Left and Right respectively. If you have more than two, you can implement an enum manually, or set Either.

Or you can return Box<dyn Iterator<Item = Whatever>>.

1 Like

I think beyond the first issue (different types returned by the different branches of the if/else there is also a problem with ownership.

In a first attempt I was able to get this to compile:

fn main() {
    let line = String::new(); // assume string has content
    let line: String = line
        .bytes()
        .flat_map(|b| {
            if b > 0x7F {
                format!("M-{}", char::from(b - 0x7F)).into_bytes()
            } else {
                vec![b]
            }
        })
        .map(|b| char::from(b))
        .collect();
}

(Playground)

I think this is probably not the prettiest solution because it allocates intermediate vectors in the loop. It might help you as a compiling starting point, however.

Either helped. Thank you!

Here's a version that doesn't allocate:

fn main() {
    let line = String::from("áÁbBcCeEéÉfF");
    let line: String = line
        .bytes()
        .flat_map(|b| {
            if b > 0x7F {
                Left("M-".bytes().chain(once(b - 0x7F)))
            } else {
                Right(once(b))
            }
        })
        .map(char::from)
        .collect();
        
    println!("{}", line);
}
1 Like

The auto_enums crate can also be used to create bespoke anonymous enums.