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
// 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
// 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-bitfieldorpacked_struct -
Am I doing binary protocol work? → Consider
deku - Is the manual approach really simpler?
Related Rules
mem-01: Choose appropriate data layoutffi-13: Ensure consistent data layout
你可能感兴趣的文章
Do Not Expose Raw Pointers in Public APIs
Unsafe Code Authors Must Verify Safety Invariants
Ensure Consistent Data Layout for Custom Types
Add Safety Section in Docs for Public Unsafe Functions
Do Not Use Union Variants Across Different Lifetimes
Use MaybeUninit
Ensure C-ABI Compatibility for Strings Between Rust and C
Do Not Dereference Pointers Cast to Misaligned Types
Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types