Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Structs of booleans as a 1-byte bitmask

If you have the following struct of booleans, Rust Analyzer says it takes up 3 bytes because there’s three fields.

struct Mask {
    field_1: bool,
    field_2: bool,
    field_3: bool,
} // size = 3, align = 1
  • Why doesn’t the compiler optimize this to a 1-byte bitmask?
  • How can I make the struct a true bitmask?

As far as I can tell the APIs will all be identical (e.g., getting/setting the fields + methods).

0000_0000
      ^^^ field_1
      |L_ field_2
      L__ field_3

P.S. I realize the modular_bitfield crate can make the Mask struct into a true bitmask, but it seems like overkill to use an external crate for something simple like this.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Because bitmasks can actually be slower and more complex than byte-level manipulation. There is no "read the 3rd byte from location X in memory" instruction. If your struct is stored as three bytes, starting at position X and you want to access field_2, then you simply have to access position X+1 in memory. That’s easy, and that’s one instruction on modern systems.

If your struct is stored as three bits and you want to access field_2, then you have to access the whole bitmask (at position X), load it into a register, and then do a bitwise my_value & 2 to get the value you want. And then, if you want it as a normalized Boolean (0 and 1, as opposed to 0 and "some undetermined nonzero value"), you have to bitshift that as well. That’s two or three instructions per access, and the same will be true of modifications. There’s hidden complexity.

And even if we were willing to pay that cost, it would completely redefine Rust’s reference semantics. At the kernel level, you can’t address an individual bit of memory; you can only have pointers to bytes. So you wouldn’t be able to take a reference to field_2 or field_3 since they’re not byte-aligned (and the reference to field_1 would be a reference to the whole struct). So Rust’s references would have to be some form of "fat pointer", storing an OS pointer together with some offset data, which would add a ton of complexity to the already complex borrowing mechanics of Rust.

I am not aware of any built-in Rust way to do this. Zig supports this, with packed struct, but in Rust I believe you’ll end up pulling in an external library. If it truly is a struct of 3 bytes (as opposed to a more complex example that you’ve distilled down for us), then I’d say let Rust do its thing and forget about the bit packing. But if you’ve benchmarked this and it really is a bottleneck, then look into some external crates.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading