#bit-field #bit #struct #proc-macro #macro #statically-typed

no-std bit-struct

Define structs which have fields which are assigned to individual bits, not bytes

37 releases

0.4.0 Mar 2, 2023
0.3.1 Feb 7, 2023
0.3.0 Nov 8, 2022
0.1.31 Jan 25, 2022
0.1.2 Nov 30, 2021

#46 in No standard library

Download history 600/week @ 2024-07-28 486/week @ 2024-08-04 694/week @ 2024-08-11 606/week @ 2024-08-18 549/week @ 2024-08-25 760/week @ 2024-09-01 727/week @ 2024-09-08 430/week @ 2024-09-15 725/week @ 2024-09-22 681/week @ 2024-09-29 192/week @ 2024-10-06 260/week @ 2024-10-13 288/week @ 2024-10-20 368/week @ 2024-10-27 285/week @ 2024-11-03 350/week @ 2024-11-10

1,302 downloads per month
Used in 2 crates

MIT/Apache

66KB
1.5K SLoC

bit-struct

crates.io codecov Minimum rustc version

Bit struct is a crate which allows for ergonomic use of C-like bit fields without mediocre IDE support resulting from proc macros. In addition, everything is statically typed checked!

Take the following example

use bit_struct::*; 

enums! {
    // 2 bits, i.e., 0b00, 0b01, 0b10
    pub HouseKind { Urban, Suburban, Rural}
}

bit_struct! {
    // u8 is the base storage type. This can be any multiple of 8
    pub struct HouseConfig(u8) {
        // 2 bits
        kind: HouseKind,
        
        // two's compliment 3-bit signed number
        lowest_floor: i3,
        
        // 2 bit unsigned number
        highest_floor: u2,
    }
}

// We can create a new `HouseConfig` like such:
// where all numbers are statically checked to be in bounds.
let config = HouseConfig::new(HouseKind::Suburban, i3!(-2), u2!(1));

// We can get the raw `u8` which represents `config`:
let raw: u8 = config.raw();
assert_eq!(114_u8, raw);

// or we can get a `HouseConfig` from a `u8` like:
let mut config: HouseConfig = HouseConfig::try_from(114_u8).unwrap();
assert_eq!(config, HouseConfig::new(HouseKind::Suburban, i3!(-2), u2!(1)));
// We need to unwrap because `HouseConfig` is not valid for all numbers. For instance, if the
// most significant bits are `0b11`, it encodes an invalid `HouseKind`. However, 
// if all elements of a struct are always valid (suppose we removed the `kind` field), the struct will
// auto implement a trait which allows calling the non-panicking:
// let config: HouseConfig = HouseConfig::exact_from(123_u8);

// We can access values of `config` like so:
let kind: HouseKind = config.kind().get();

// And we can set values like so:
config.lowest_floor().set(i3!(0));

// We can also convert the new numeric types for alternate bit-widths into the 
// numeric types provided by the standard library:
let lowest_floor: i3 = config.lowest_floor().get();
let lowest_floor_std: i8 = lowest_floor.value();
assert_eq!(lowest_floor_std, 0_i8);

Benefits

  • No proc macros
  • Autocompletion fully works (tested in IntelliJ Rust)
  • Fast compile times
  • Statically checks that structs are not overfilled. For example, these overfilled structs will not compile:
      bit_struct::bit_struct! {
          struct TooManyFieldsToFit(u16) {
              a: u8,
              b: u8,
              c: bit_struct::u1
          }
      }
    
      bit_struct::bit_struct! {
          struct FieldIsTooBig(u16) {
              a: u32
          }
      }
    
  • Statically checked types

Further Documentation

Look at the integration tests in the tests folder for further insight.

Dependencies

~0.4–1MB
~23K SLoC