--- id: mem-05 original_id: P.UNS.MEM.05 level: P impact: MEDIUM --- # Use Third-Party Crates for Bitfields ## Summary Use crates like `bitflags`, `bitvec`, or `modular-bitfield` instead of manual bit manipulation for complex bitfield operations. ## Rationale - Manual bit manipulation is error-prone - Easy to get offsets, masks, or endianness wrong - Crates provide type-safe, tested abstractions - Proc-macro crates generate efficient code ## Bad Example ```rust // DON'T: Manual bitfield manipulation struct Flags(u32); impl Flags { const READ: u32 = 1 << 0; const WRITE: u32 = 1 << 1; const EXECUTE: u32 = 1 << 2; fn has_read(&self) -> bool { (self.0 & Self::READ) != 0 } fn set_read(&mut self) { self.0 |= Self::READ; } fn clear_read(&mut self) { self.0 &= !Self::READ; // Easy to forget the ! } } // DON'T: Manual packed bitfields for FFI #[repr(C)] struct PackedHeader { data: u32, } impl PackedHeader { // Error-prone: wrong shift or mask values fn version(&self) -> u8 { ((self.data >> 24) & 0xFF) as u8 } fn flags(&self) -> u16 { ((self.data >> 8) & 0xFFFF) as u16 } fn tag(&self) -> u8 { (self.data & 0xFF) as u8 } } ``` ## Good Example ```rust // DO: Use bitflags for flag sets use bitflags::bitflags; bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Flags: u32 { const READ = 1 << 0; const WRITE = 1 << 1; const EXECUTE = 1 << 2; const RW = Self::READ.bits() | Self::WRITE.bits(); } } fn use_flags() { let mut flags = Flags::READ | Flags::WRITE; flags.insert(Flags::EXECUTE); flags.remove(Flags::WRITE); if flags.contains(Flags::READ) { println!("Readable"); } } // DO: Use modular-bitfield for packed structures use modular_bitfield::prelude::*; #[bitfield] #[repr(C)] struct PackedHeader { tag: B8, // 8 bits flags: B16, // 16 bits version: B8, // 8 bits } fn use_packed() { let header = PackedHeader::new() .with_version(1) .with_flags(0x1234) .with_tag(0xAB); assert_eq!(header.version(), 1); assert_eq!(header.flags(), 0x1234); } // DO: Use bitvec for arbitrary bit manipulation use bitvec::prelude::*; fn use_bitvec() { let mut bits = bitvec![u8, Msb0; 0; 16]; bits.set(0, true); bits.set(7, true); let byte: u8 = bits[0..8].load_be(); assert_eq!(byte, 0b1000_0001); } ``` ## Recommended Crates | Crate | Use Case | Features | |-------|----------|----------| | `bitflags` | Flag sets (like C enums) | Type-safe, const, derives | | `modular-bitfield` | Packed struct fields | Proc macro, repr(C) | | `bitvec` | Arbitrary bit arrays | Slicing, iteration | | `packed_struct` | Binary protocol structs | Endianness, derive | | `deku` | Binary parsing | Derive, read/write | ## Checklist - [ ] Am I manipulating multiple bit flags? → Use `bitflags` - [ ] Am I packing fields into bytes? → Use `modular-bitfield` or `packed_struct` - [ ] Am I doing binary protocol work? → Consider `deku` - [ ] Is the manual approach really simpler? ## Related Rules - `mem-01`: Choose appropriate data layout - `ffi-13`: Ensure consistent data layout