Procedural macro for declarative defining bitfields similar to structs.
As this library provides a procedural macro, it has no runtime dependencies and works for no-std.
fmt::DebugAdd this to your Cargo.toml:
toml
[dependencies]
bitfield-struct = "0.4"
Let's begin with a simple example. Suppose we want to store multiple data inside a single Byte, as shown below:
| 7 | 6 | 5 | 4 | 3 | 3 | 1 | 0 | 
| P | Level | S | Kind | ||||
This crate generates a nice wrapper type that makes it easy to do this:
```rust /// Define your type like this with the bitfield attribute
struct MyByte {
    /// The first field occupies the least significant bits
    #[bits(4)]
    kind: usize,
    /// Booleans are 1 bit large
    system: bool,
    /// The bits attribute specifies the bit size of this field
    #[bits(2)]
    level: usize,
    /// The last field spans over the most significant bits
    present: bool
}
// The macro creates three accessor functions for each field:
// 
assert!(my_byte.present()); ```
Additionally, this crate has a few useful features, which are shown here in more detail.
The example below shows how attributes are carried over and how signed integers, padding, and custom types are handled.
```rust /// A test bitfield with documentation
bitfield are carried overstruct MyBitfield {
    /// defaults to 16 bits for u16
    int: u16,
    /// interpreted as 1 bit flag
    flag: bool,
    /// custom bit size
    #[bits(1)]
    tiny: u8,
    /// sign extend for signed integers
    #[bits(13)]
    negative: i16,
    /// supports any type that implements From<u64> and Into<u64>
    #[bits(16)]
    custom: CustomEnum,
    /// public field -> public accessor functions
    #[bits(12)]
    pub public: usize,
    /// padding
    #[bits(5)]
    p: u8,
    /// zero-sized members are ignored
    #[bits(0)]
    _completelyignored: String,
}
/// A custom enum
enum CustomEnum {
    A = 0,
    B = 1,
    C = 2,
}
// implement From<u64> and Into<u64> for CustomEnum!
// Usage: let mut val = MyBitfield::new() .withint(3 << 15) .withflag(true) .withtiny(1) .withnegative(-3) .withcustom(CustomEnum::B) .withpublic(2);
println!("{val:?}"); let raw: u64 = val.into(); println!("{raw:b}");
asserteq!(val.int(), 3 << 15); asserteq!(val.flag(), true); asserteq!(val.negative(), -3); asserteq!(val.tiny(), 1); asserteq!(val.custom(), CustomEnum::B); asserteq!(val.public(), 2);
// const members asserteq!(MyBitfield::FLAGBITS, 1); asserteq!(MyBitfield::FLAGOFFSET, 16);
val.setnegative(1); asserteq!(val.negative(), 1); ```
The macro generates three accessor functions for each field. Each accessor also inherits the documentation of its field.
The signatures for int are:
```rust // generated struct struct MyBitfield(u64); impl MyBitfield { const fn new() -> Self { Self(0) }
const INT_BITS: usize = 16;
const INT_OFFSET: usize = 0;
const fn with_int(self, value: u16) -> Self { /* ... */ }
const fn int(&self) -> u16 { /* ... */ }
fn set_int(&mut self, value: u16) { /* ... */ }
// other field ...
}
// generated trait implementations
impl From
Hint: You can use the rust-analyzer "Expand macro recursively" action to view the generated code.
fmt::DebugThis macro automatically creates a suitable fmt::Debug implementation
similar to the ones created for normal structs by #[derive(Debug)].
You can disable it with the extra debug argument.
```rust
struct CustomDebug { data: u64 } impl fmt::Debug for CustomDebug { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "0x{:x}", self.data()) } }
let val = CustomDebug::new().with_data(123); println!("{val:?}") ```