Rust Bit Flags
Bit flags pack a set of yes/no switches into one number, with one bit per switch. The bitflags crate gives those bits names and safe verbs.
One bool per switch gets clumsy
Three switches as three separate bools — every test names them all, and every function must take them all:
fn main() {
let can_read = true;
let can_write = false;
let can_run = true;
if can_read && !can_write && can_run {
println!("read and run, no write");
}
println!("three separate bools to carry everywhere");
}Name the switches
Each const owns one bit. The struct is one u8 underneath — the names are the whole improvement over a bare number.
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Perms: u8 {
const READ = 0b001;
const WRITE = 0b010;
const EXECUTE = 0b100;
}
}Combine and test
| joins switches into a set. contains asks one question.
let mine = Perms::READ | Perms::WRITE;
println!("can write: {}", mine.contains(Perms::WRITE));
println!("can execute: {}", mine.contains(Perms::EXECUTE));Add and remove one switch
insert turns one on, remove turns one off — the other switches are untouched.
let mut p = Perms::READ;
p.insert(Perms::EXECUTE);
p.remove(Perms::READ);
println!("{:?}", p);The raw number underneath
bits() reads the packed integer — handy for saving or sending the whole set at once.
println!("{}", (Perms::READ | Perms::WRITE).bits());
println!("{}", Perms::all().bits());Example
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Perms: u8 {
const READ = 0b001;
const WRITE = 0b010;
const EXECUTE = 0b100;
}
}
fn main() {
let mut guest = Perms::READ;
println!("guest starts: {:?}", guest);
println!("guest can read: {}", guest.contains(Perms::READ));
println!("guest can write: {}", guest.contains(Perms::WRITE));
guest.insert(Perms::WRITE);
println!("after upgrade: {:?} (bits {})", guest, guest.bits());
guest.toggle(Perms::EXECUTE);
println!("after toggle: {:?}", guest);
guest.remove(Perms::READ);
println!("final: {:?} (bits {})", guest, guest.bits());
}Key points
- Add bitflags = "2" under [dependencies] in Cargo.toml.
- bitflags! is a macro from the crate.
- The macro writes the whole type.
- The generated type includes the | combination and the verbs used here.
- Each flag must own exactly one bit — 0b001, 0b010, 0b100 (1, 2, 4) — so combining them never mixes meanings.
- contains, insert, remove and toggle are the everyday verbs.
- | joins sets of flags in one expression.
- from_bits(number) rebuilds flags from a saved number.
- from_bits returns None if the number holds bits no flag owns.
- from_bits_truncate quietly drops the unknown bits instead.
- File permissions, feature toggles, and game options all fit this shape.
The guest starts with only READ. insert adds WRITE without touching READ, so the set reads Perms(READ | WRITE) with bits 3 (0b011). toggle flips EXECUTE on, and remove drops READ, leaving WRITE | EXECUTE = bits 6 (0b110). The Debug form always lists exactly the switches that are on.
Note
Flags are Copy and tiny: the whole set of options travels as one cheap integer, and that is the point of the packing.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Flags are READ = 0b001, WRITE = 0b010, EXECUTE = 0b100. What is (READ | WRITE).bits()?