[Solved] Flat_map and Result issue


#1

Hi,

I’m creating my first real rust app and I am having little trouble with iterators and flat_map.

I’m creating a simple z85 encoder and decoder. Before error handling I had:

fn from_b85_chunk(data: &[u8])) -> u32;
fn to_byte_chunk(data: u32) -> Vec<u8>;

And i did

// data is a slice
data.chunks(5).map(from_b85_chunk).flat_map(to_byte_chunk).collect::<Vec<u8>>();

I want to introduce error handling so i created something like this

enum DecodeError;

fn from_b85_chunk(data: &[u8]) -> Result<u32, DecodeError>;
fn to_byte_chunk(data: u32) -> Vec<u8>;

I can now do the following:

let p = data.chunks(5).map(from_b85_chunk).collect::<Result<Vec<_>, _>>();
try!(p).iter().flat_map(to_byte_chunk).collect::<Vec<_>>()

Is there a way to do this without the collect in between from_b85_chunk and to_byte_chunk?


#2

I tried doing something like this

fn to_byte_chunk(data: Result<u32, DecodeError>) -> Result<Vec<u8>, DecodeError>;

and then

 data.chunks(5).map(from_b85_chunk).flat_map(to_byte_chunk).collect::<Result<Vec<_>, _>>()

But I can’t seem to make the collect work. I think that the issue is in the use of flat_map.


#3

I would like to do the following, but the match arms have incompatible types here.

let iter = data.chunks(5) // Iterator<&[u8]>
    .map(from_b85_chunk) // Iterator<Result<u32, _>>
    .map(to_byte_chunk); // Iterator<Result<Vec<u8>, _>>
    .flat_map(|res| {
        // Match arms have incompatible types (different concrete iterators). :( 
        match res { // Result<Vec<u8>, _>
            Ok(v) => v.iter().map(|x| Ok(*x)), // Iterator<Result<u8, _>> - all Ok
            Err(e) => std::iter::once(e) // Iterator<Result<u8, _>> - Err
        }
    }) // Iterator<Result<u8, DecodeError>>
    .collect::<Result<Vec<_>, _>>()

So instead, just write a for loop to do the flat_map yourself. It’s short enough.

let data = vec![1u8,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
let iter = data.chunks(5) // Iterator<&[u8]>
    .map(from_b85_chunk) // Iterator<Result<u32, _>>
    .map(to_byte_chunk); // Iterator<Result<Vec<u8>, _>>

let mut vec = Vec::new();
for res_vec in iter { // Result<Vec<u8>, _>
    vec.extend(try!(res_vec));
}
Ok(vec)

Playground link

Ideally, though, to_byte_chunk would return [u8; 4] or something iterable that doesn’t require a heap allocation — the code above would be the same. Otherwise, you might as well have to_byte_chunk just push into a provided mut vec as well for the sake of efficiency.


#4

Thanks. I got so wrapped up in trying to use map or something similar that I forgot that you can use a simple for.