1
\$\begingroup\$

I am a Rust newbie and I am not familiar with all the iterator options. This is what I have so far. How can I make this better or at least avoid collecting twice into a Vec?

let num: u16 = 0b0010001000100010; // input number
let bin = format!("{:016b}", num);

let parsed = bin
    .split("")
    .filter_map(|s| s.parse().ok())
    .collect::<Vec<u8>>();

let mat = parsed.chunks(4).collect::<Vec<_>>();

println!("{:?}", mat); 

// outputs [[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]]
\$\endgroup\$
0
\$\begingroup\$

One thing I'm going to do first is create an enum with only two values. This represents a binary value and is more memory-efficient than passing around a bunch of u8s. This way 16 Bits can be represented as 16 actual bits in memory (though this isn't guaranteed).

/// a single bit
#[derive(Clone, Copy, Debug)]
enum Bit {
    /// 1, high
    H1 = 1,
    /// 0, low
    L0 = 0,
}
use Bit::*; // allow us to just use `L0` and `H1` without the `Bit::` prefix

It is likely much faster to split a number into bits using numerical operators. There are a couple ways of doing it.

  1. Iterating a mask

With this we increase our mask each time, building an array from it. The 15 - i is there because we want the MSB at index 0.

/// convert a number to 16 bits by sliding a mask across it
fn into_bits_mask(num: u16) -> [Bit; 16] {
    let mut out = [L0; 16];

    for i in 0..16 {
        out[15 - i] = if num & (1u16 << i) > 0 {
            H1
        } else {
            L0
        };
    }

    out
}
  1. Shifting the number with a static mask

This is essentially the same thing, but we shift the number instead of the mask.

/// convert a number to 16 bits by right-shifting it
fn into_bits_shift(num: u16) -> [Bit; 16] {
    let mut out = [L0; 16];

    for i in 0..16 {
        out[15 - i] = if (num >> i) & 1u16 > 0 {
            H1
        } else {
            L0
        };
    }

    out
}

We can then modify these to output an array of 4 nibbles.

/// convert a number to 4 nibbles by sliding a mask across it
fn into_nibbles_mask(num: u16) -> [[Bit; 4]; 4] {
    let mut out = [[L0; 4]; 4];

    for i in 0..16 {
        let mask = 1u16 << (15 - i);
        out[i / 4][i % 4] = if num & mask > 0 {
            H1
        } else {
            L0
        };
    }

    out
}

/// convert a number to 4 nibbles by right-shifting it
fn into_nibbles_shift(num: u16) -> [[Bit; 4]; 4] {
    let mut out = [[L0; 4]; 4];

    for i in 0..16 {
        out[i /4][i % 4] = if (num >> (15 - i)) & 1u16 > 0 {
            H1
        } else {
            L0
        };
    }

    out
}

There is room for more optimization here, of course.

Here's a working example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=b010e34728d554e995e0de4ddb4b1eed

EDIT: I was asked about using iterators, so here's my most iterator-function styled method

/// convert a number to 4 nibbles using iterator methods
fn into_nibbles_iter(num: u16) -> Vec<Vec<Bit>> {
    // split into nibbles
    [num >> 12, num >> 8, num >> 4, num]
        .iter()
        .map(|nibble| {
            // mask off each bit
            [nibble & 8, nibble & 4, nibble & 2, nibble & 1]
                .iter()
                // convert to Bits
                .map(|b| if b > &0 { H1 } else { L0 })
                .collect()
        })
        .collect()
}

And here's a new playground link demonstrating it: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=5ecda81379c3cab749709f551109adfb

\$\endgroup\$
  • \$\begingroup\$ This is amazing @PitaJ and thanks for the working example. I am curious about doing it using iterators regardless of performance \$\endgroup\$ – matharumanpreet00 Oct 25 '18 at 16:04
  • \$\begingroup\$ @matharumanpreet00 I've added an iterator-styled method. Does that answer your question? \$\endgroup\$ – PitaJ Oct 25 '18 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.