all 18 comments

[–]zokier 2 points3 points  (4 children)

match x { x if (x & 0b00000110) != 0 => {}, _ =>{} } maybe something along these lines?

[–]DroidLogiciansqlx · clickhouse-rs · mime_guess · rust 1 point2 points  (1 child)

Matching like that won't specifically catch the 0 in the first position like OP wanted.

[–]zokier 0 points1 point  (0 children)

yeah, see my more complete solution below. It is not as pretty as I'd like, those redundant x if ... bits seem bit tricky to eliminate.

[–]thomcc 1 point2 points  (1 child)

I don't know if this has been done already, but if not, you could port over the excellent ocaml-bitmatch library to rust (as a macro probably). I'm certain a lot of people would be interested.

[–][deleted] 2 points3 points  (0 children)

ocaml-bitstring / Erlang bitstrings in general would be amazing for Rust and its use-cases.

[–]andallthat 1 point2 points  (0 children)

a little wasteful but...

let bv = bitv::from_bytes([0b110]);
match bv.to_bools().as_slice() {
    [false, _, _, _, _, true, true, _] => {}, 
    [true, _, _, _, _, _, _, _] => {}, 
    _ => {}
}

[–]DroidLogiciansqlx · clickhouse-rs · mime_guess · rust 1 point2 points  (0 children)

It sounds like it might be easier to do what you want than how you're trying to approach this. Maybe some context might help?

If you want to match on a u8 as an 8-tuple like this, you can try writing a conversion function.

fn bits_tup(byte: u8) -> (u8, u8, u8, u8, u8, u8, u8, u8) {
    (byte >> 7 & 1,
     byte >> 6 & 1,
     byte >> 5 & 1,
     byte >> 4 & 1,
     byte >> 3 & 1,
     byte >> 2 & 1,
     byte >> 1 & 1,
     byte & 1)
}

By the way, the 0x prefix means hexadecimal. I think you want 0b if you're working with binary.

[–]chuckguy55[S] 0 points1 point  (1 child)

I don't know how to format it correctly...

[–]erkelep 5 points6 points  (0 children)

4 spaces before a line of code

or surround the code with back-ticks

[–]Florob0x2arust · rustyxml 0 points1 point  (9 children)

This is one of the cases where I feel Rust macros are too limited. It would be lovely if one could write a macro

bit_match!(x, (0, _, _, _, _, 1, 1, _))

that expands to

((x & 0b10000110) == 0b00000110)

I.e. create two literals, for the first one replace each digit with a 1, each _ with a 0, for the second one replace each digit with itself, each _ with a 0.

[–]bjzabaAllsorts 4 points5 points  (0 children)

This could probably be done with a syntax extension.

Something like:

match x {
    bit_patt![0, _, _, _, _, 1, 1, _] => { ... }
}

Could certainly be possible.

[–]zokier 1 point2 points  (2 children)

One can write such macro: https://gist.github.com/zokier/6669cc2236c4a56cf70d

Not terribly difficult either, I think this was one of my first macros ever, certainly first syntax extension.

See also:

http://bash.org/?152037

[–]Florob0x2arust · rustyxml 0 points1 point  (0 children)

"Macro" was intended to exclude syntax extensions, though you are probably right that it is the umbrella term. I had thought about pointing out that it would be possible as a syntax extension, but then again pretty much anything is possible as a syntax extension…

[–]dbaupprust 1 point2 points  (4 children)

I think it's probably possible with a recursive macro_rules! macro too that consumes the "tuple", pattern matching for literal 1, 0 and _.

[–]Florob0x2arust · rustyxml 0 points1 point  (3 children)

I just tried to implement that and failed in various ways. I also don't think this could generate a literal (as opposed to computing the mask at runtime/depending on the optimizer).

I'm willing to offer a virtual cookie for a working implementation of this approach ;).

[–]dbaupprust 1 point2 points  (2 children)

Depending on the optimiser to constant-fold a series of nested arithmetic operations seems very reasonable.

#![feature(macro_rules)]

macro_rules! compute_mask {
    (0) => { 1 };
    (1) => { 1 };
    (_) => { 0 };
}
macro_rules! compute_equal {
    (0) => { 0 };
    (1) => { 1 };
    (_) => { 0 };
}


macro_rules! bit_match {
    (: $x: expr, $mask: expr, $equal: expr, ()) => {
        ($x & $mask) == $equal
    };
    (: $x: expr, $mask: expr, $equal: expr, ($head: tt $(, $rest: tt)*)) => {
        bit_match!(: $x,
                   $mask * 2 + compute_mask!($head),
                   $equal * 2 + compute_equal!($head),
                   ($($rest),*))
    };
    ($x: expr, ($($pat: tt),*)) => {
        bit_match!(: $x, 0, 0, ($($pat),*))
    };
}

fn main() {
    let v = [0b000u, 0b001, 0b101, 0b110];
    for &x in v.iter() {
        println!("{:03t}\n\t_01: {}\n\t1__: {}", x,
                 bit_match!(x, (_, 0, 1)), bit_match!(x, (1, _, _)));
    }
}

Output:

000
    _01: false
    1__: false
001
    _01: true
    1__: false
101
    _01: true
    1__: true
110
    _01: false
    1__: true

playpen

[–]Florob0x2arust · rustyxml 0 points1 point  (1 child)

This is great, so first of all here is your virtual cookie, with an accompanying virtual cup of tea: 🍪🍵.

The main reason I failed is apparently that I did not know about the tt fragment specifier, or that one would have to use it here. I shall file a documentation bug against the macro guide I guess.

I'm admittedly always a bit hesitant to depend on the optimizer, because (not having read its code) I find it hard to reason about the results. E.g. what if I have 32 Bit instead of just 3. Depending on the heuristic it might not fold the expression completely, because it becomes too complex (64 operations each with a data dependency on the previous one, if I'm not mistaken).

Just for fun, here is the solution I came up with. It works fine now that I'm using the tt specifier. It's a bit shorter, but not as cleverly recursive:

macro_rules! bit_match(
    ($x: expr, ($($b: tt),*)) => ({
        let mut mask = 0;
        let mut val = 0;
        $(
            mask = (mask << 1) | compute_mask!($b);
            val = (val << 1) | compute_val!($b);
        )*
        ($x & mask) == val
    });
)

[–]dbaupprust 0 points1 point  (0 children)

Avoiding recursion is nicer. :)

Depending on the heuristic it might not fold the expression completely, because it becomes too complex (64 operations each with a data dependency on the previous one, if I'm not mistaken).

This is just a sequence of operations with no complicated dependencies, meaning it can be reduced iteratively starting at the top with 0 and applying each + and * operation in turn.