Idiomatic Rust matches with pattern guards


I’m quite new to Rust, and am working on a virtual machine image decompiler. I have some working code that uses match to check opcode numbers, but it doesn’t feel very idiomatic, and am wondering what alternatives I should consider.

First up is the cleanest case:

    // Stop, or next instruction?
    let next = match opcode {
        28 | 139 | 140 | 176 | 177 | 179 | 181 ..= 184 | 186 | 188 | 228 | 243 | 244 | 246 => None,
        _ => Some(image.position() as u32),
    };

I have only two paths here, and no guards. This seems like the kind of situation that match is ideal for. However, it is a lot of magic numbers.

This following match needs to guarded patterns because some of the opcodes change structure based on the image version. The same variable will be stored in each case however, so I used a match to produce a bool, and then took that to an if branch. Originally I just had a simple match statement and duplicated the Some(image.get_u8()). I’m still not sure that it wasn’t better that way.

    // Store variable
    let store = if match opcode {
        8 | 9 | 15 ..= 25 | 129 ..= 132 | 136 | 142 | 143 | 224 | 231 | 236 | 246 ..= 248 |
            1000 ..= 1004 | 1009 | 1010 | 1012 | 1030 => true,
        181 | 182 if version == 4 => true,
        185 | 228 if version >= 5 => true,
        _ => false,
    } {
        Some(image.get_u8())
    } else {
        None
    };

Lastly we have a match with only one guarded pattern, but a much more complex calculation to follow, so I didn’t want to just duplicating the code for each pattern.

    // Branch offset
    let branch = if match opcode {
        1 ..= 7 | 10 | 128 ..= 130 | 140 | 189 | 191 | 247 | 255 => true,
        181 | 182 if version == 3 => true,
        _ => false,
    } {
        let first_branch_byte = image.get_u8();
        let iftrue = first_branch_byte & 0x80 != 0;
        let twobytes = first_branch_byte & 0x40 == 0;
        let offset: i16 = if twobytes {
            (((((first_branch_byte & 0x3F) as u16) << 8) | image.get_u8() as u16) << 2) as i16 >> 2
        } else {
            (first_branch_byte & 0x3F) as i16
        };
        Some(Branch {
            iftrue,
            offset,
        })
    } else {
        None
    };

Because of all the magic numbers, I have considered whether it would be better to try to make some macros to produce the patterns from data elsewhere, or if I should just test for data in a HashMap instead of using match.