Avoiding allocations with flat_map()

I'm wondering whether it's possible to avoid small allocations when map produces more outputs than inputs. Here, I'm iterating over bytes one at a time and am producing two bytes during one of the steps.

fn binary_to_hex<'collection, T: IntoIterator<Item = &'collection u8>>(binary: T) -> String {
    const HEX_DIGITS: &'static [u8] = b"0123456789ABCDEF";

    let hex: Vec<u8> = binary
        .into_iter()
        .flat_map(|byte| [byte >> 4, byte & 0xF].to_vec()) // can I remove this to_vec() call? 
        .map(|nibble| HEX_DIGITS[nibble as usize] )
        .collect();

    // safe because it is only made from HEX_DIGITS
    unsafe {
        String::from_utf8_unchecked(hex)
    }
}

fn main() {
    let mess = binary_to_hex(b"\x12\xF9\x13\x02\xA9\x00\x91\x02\x23");
    println!("{:?}", mess);
}

(Playground)

It won't be necessary when IntoIterator will be implemented for arrays themselfs https://github.com/rust-lang/rust/issues/65798

For now you can use the arrayvec crate, replacing [..].to_vec() with ArrayVec::from([..])

1 Like

You can also do std::iter::once(byte >> 4).chain(std::iter::once(byte & 0xF)). Or maybe just push in a for loop.

3 Likes

That's a really nice approach. Thank you :heart: