How do I define bitwise flags with bitflags in Rust?

Walkthrough

The bitflags crate provides a macro for defining types that represent sets of bitwise flags. It generates types that support combining flags with |, intersection with &, and checking membership with contains(). The resulting type is a wrapper around a primitive integer, making it efficient for storage and transmission. This is perfect for representing options, permissions, capabilities, or any set of boolean flags that need to be combined.

Key concepts:

  1. Bit positions — each flag occupies a unique bit position
  2. Bitwise operations — combine with |, intersect with &, toggle with ^
  3. Type safety — the macro generates a proper type with methods
  4. Empty and all — special values for no flags and all flags
  5. Serialization — optional serde support for JSON/binary formats

Code Example

# Cargo.toml
[dependencies]
bitflags = "2.0"
use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Permissions: u32 {
        const READ = 0b0001;
        const WRITE = 0b0010;
        const EXECUTE = 0b0100;
        const ADMIN = 0b1000;
    }
}
 
fn main() {
    // Combine flags
    let perms = Permissions::READ | Permissions::WRITE;
    println!("Permissions: {:?}", perms);
    
    // Check if flag is set
    println!("Has READ: {}", perms.contains(Permissions::READ));
    println!("Has EXECUTE: {}", perms.contains(Permissions::EXECUTE));
}

Basic Flag Definition

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    struct Flags: u32 {
        // Each flag is a single bit
        const A = 0b00000001;
        const B = 0b00000010;
        const C = 0b00000100;
        const D = 0b00001000;
        
        // Composite flags (multiple bits)
        const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
        const ALL = 0b00001111;
    }
}
 
fn main() {
    // Create empty flags
    let empty = Flags::empty();
    println!("Empty: {:?}", empty);
    println!("Is empty: {}", empty.is_empty());
    
    // Create with all flags
    let all = Flags::all();
    println!("All: {:?}", all);
    
    // Single flag
    let a = Flags::A;
    println!("Just A: {:?}", a);
    
    // Combine flags
    let ab = Flags::A | Flags::B;
    println!("A | B: {:?}", ab);
    
    // Access raw bits
    println!("Bits of A: {}", Flags::A.bits());
    println!("Bits of A|B: {}", ab.bits());
}

Checking Flag Membership

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Status: u8 {
        const ACTIVE = 0b0001;
        const VISIBLE = 0b0010;
        const LOCKED = 0b0100;
        const ARCHIVED = 0b1000;
    }
}
 
fn main() {
    let status = Status::ACTIVE | Status::VISIBLE;
    
    // contains: check if a single flag is set
    println!("Is active: {}", status.contains(Status::ACTIVE));
    println!("Is locked: {}", status.contains(Status::LOCKED));
    
    // contains all: check if multiple flags are set
    println!("Has both ACTIVE and VISIBLE: {}", 
        status.contains(Status::ACTIVE | Status::VISIBLE));
    
    // intersects: check if any of the specified flags are set
    println!("Intersects ACTIVE|LOCKED: {}", 
        status.intersects(Status::ACTIVE | Status::LOCKED));
    println!("Intersects LOCKED|ARCHIVED: {}", 
        status.intersects(Status::LOCKED | Status::ARCHIVED));
    
    // is_empty: check if no flags are set
    println!("Is empty: {}", status.is_empty());
    println!("Is all: {}", status.is_all());
}

Modifying Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Options: u16 {
        const A = 1 << 0;
        const B = 1 << 1;
        const C = 1 << 2;
        const D = 1 << 3;
    }
}
 
fn main() {
    let mut flags = Options::A | Options::C;
    println!("Initial: {:?}", flags);
    
    // Insert a flag (set bit)
    flags.insert(Options::B);
    println!("After insert B: {:?}", flags);
    
    // Remove a flag (clear bit)
    flags.remove(Options::A);
    println!("After remove A: {:?}", flags);
    
    // Toggle a flag
    flags.toggle(Options::C);
    println!("After toggle C: {:?}", flags);
    flags.toggle(Options::C);
    println!("After toggle C again: {:?}", flags);
    
    // Set all flags
    flags.set(Options::A | Options::B | Options::C | Options::D, true);
    println!("After set all: {:?}", flags);
    
    // Set specific combination
    flags.set(Options::A | Options::C, false);
    println!("After clearing A|C: {:?}", flags);
}

Bitwise Operators

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Perm: u8 {
        const READ = 1;
        const WRITE = 2;
        const EXECUTE = 4;
    }
}
 
fn main() {
    let read_write = Perm::READ | Perm::WRITE;
    let write_exec = Perm::WRITE | Perm::EXECUTE;
    
    // Bitwise OR (union)
    let union = read_write | write_exec;
    println!("Union: {:?}", union);
    
    // Bitwise AND (intersection)
    let intersection = read_write & write_exec;
    println!("Intersection: {:?}", intersection);
    
    // Bitwise XOR (symmetric difference)
    let diff = read_write ^ write_exec;
    println!("Symmetric difference: {:?}", diff);
    
    // Bitwise NOT (complement)
    let complement = !Perm::READ;
    println!("NOT READ: {:?}", complement);
    println!("NOT READ (filtered to valid): {:?}", complement & Perm::all());
    
    // Difference (flags in self but not in other)
    let diff2 = read_write.difference(write_exec);
    println!("READ|WRITE - WRITE|EXEC: {:?}", diff2);
    
    // Symmetric difference using method
    let sym_diff = read_write.symmetric_difference(write_exec);
    println!("Sym diff (method): {:?}", sym_diff);
}

File Permissions Example

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct FileMode: u32 {
        // Owner permissions
        const OWNER_READ = 0o400;
        const OWNER_WRITE = 0o200;
        const OWNER_EXEC = 0o100;
        
        // Group permissions
        const GROUP_READ = 0o040;
        const GROUP_WRITE = 0o020;
        const GROUP_EXEC = 0o010;
        
        // Others permissions
        const OTHER_READ = 0o004;
        const OTHER_WRITE = 0o002;
        const OTHER_EXEC = 0o001;
        
        // Common combinations
        const OWNER_ALL = Self::OWNER_READ.bits() | Self::OWNER_WRITE.bits() | Self::OWNER_EXEC.bits();
        const GROUP_ALL = Self::GROUP_READ.bits() | Self::GROUP_WRITE.bits() | Self::GROUP_EXEC.bits();
        const OTHER_ALL = Self::OTHER_READ.bits() | Self::OTHER_WRITE.bits() | Self::OTHER_EXEC.bits();
    }
}
 
impl FileMode {
    fn from_octal(mode: u32) -> Self {
        FileMode::from_bits_truncate(mode)
    }
    
    fn to_octal(&self) -> u32 {
        self.bits()
    }
}
 
fn main() {
    // Create a typical file mode (755)
    let mode = FileMode::OWNER_ALL | FileMode::GROUP_READ | FileMode::GROUP_EXEC | FileMode::OTHER_READ | FileMode::OTHER_EXEC;
    
    println!("Mode: {:o}", mode.to_octal());
    println!("Mode: {:?}", mode);
    
    // Check permissions
    println!("\nOwner can read: {}", mode.contains(FileMode::OWNER_READ));
    println!("Owner can write: {}", mode.contains(FileMode::OWNER_WRITE));
    println!("Group can write: {}", mode.contains(FileMode::GROUP_WRITE));
    
    // Parse from octal
    let parsed = FileMode::from_octal(0o644);
    println!("\nParsed 644: {:?}", parsed);
    
    // Modify permissions
    let mut new_mode = mode;
    new_mode.remove(FileMode::OWNER_EXEC | FileMode::GROUP_EXEC | FileMode::OTHER_EXEC);
    new_mode.insert(FileMode::OWNER_WRITE);
    println!("\nModified mode: {:o}", new_mode.to_octal());
}

Feature Flags Example

use bitflags::bitflags;
use std::fmt;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Features: u64 {
        const AUTH = 1 << 0;
        const LOGGING = 1 << 1;
        const CACHE = 1 << 2;
        const METRICS = 1 << 3;
        const RATE_LIMIT = 1 << 4;
        const CORS = 1 << 5;
        const HTTPS = 1 << 6;
        const WEBSOCKET = 1 << 7;
    }
}
 
impl fmt::Display for Features {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut names = Vec::new();
        if self.contains(Features::AUTH) { names.push("auth"); }
        if self.contains(Features::LOGGING) { names.push("logging"); }
        if self.contains(Features::CACHE) { names.push("cache"); }
        if self.contains(Features::METRICS) { names.push("metrics"); }
        if self.contains(Features::RATE_LIMIT) { names.push("rate-limit"); }
        if self.contains(Features::CORS) { names.push("cors"); }
        if self.contains(Features::HTTPS) { names.push("https"); }
        if self.contains(Features::WEBSOCKET) { names.push("websocket"); }
        write!(f, "[{}]", names.join(", "))
    }
}
 
fn main() {
    // Enable multiple features
    let features = Features::AUTH | Features::LOGGING | Features::HTTPS | Features::CACHE;
    
    println!("Enabled features: {}", features);
    println!("Raw bits: {:b}", features.bits());
    
    // Check features
    if features.contains(Features::AUTH) {
        println!("Authentication enabled");
    }
    
    // Add features
    let mut more_features = features;
    more_features.insert(Features::METRICS | Features::RATE_LIMIT);
    println!("\nAfter adding metrics and rate-limit: {}", more_features);
    
    // Remove features
    more_features.remove(Features::CACHE);
    println!("After removing cache: {}", more_features);
    
    // Create from bits (e.g., loaded from config)
    let from_config = Features::from_bits_truncate(0b10101010);
    println!("\nFrom config bits: {}", from_config);
}

Custom Methods

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Style: u32 {
        const BOLD = 1 << 0;
        const ITALIC = 1 << 1;
        const UNDERLINE = 1 << 2;
        const STRIKETHROUGH = 1 << 3;
        const BLINK = 1 << 4;
        const REVERSE = 1 << 5;
    }
}
 
impl Style {
    // Check if any text decoration is active
    fn has_decoration(&self) -> bool {
        !self.is_empty()
    }
    
    // Check for "heavy" styles (bold or italic)
    fn is_heavy(&self) -> bool {
        self.intersects(Style::BOLD | Style::ITALIC)
    }
    
    // Get ANSI escape code
    fn to_ansi(&self) -> String {
        let mut codes = Vec::new();
        if self.contains(Style::BOLD) { codes.push("1"); }
        if self.contains(Style::ITALIC) { codes.push("3"); }
        if self.contains(Style::UNDERLINE) { codes.push("4"); }
        if self.contains(Style::BLINK) { codes.push("5"); }
        if self.contains(Style::REVERSE) { codes.push("7"); }
        if self.contains(Style::STRIKETHROUGH) { codes.push("9"); }
        
        if codes.is_empty() {
            "\x1b[0m".to_string() // reset
        } else {
            format!("\x1b[{}m", codes.join(";"))
        }
    }
}
 
fn main() {
    let style = Style::BOLD | Style::UNDERLINE;
    
    println!("Style: {:?}", style);
    println!("Has decoration: {}", style.has_decoration());
    println!("Is heavy: {}", style.is_heavy());
    
    // Apply ANSI styling
    println!("\x1b[0mNormal text");
    println!("{}Styled text\x1b[0m", style.to_ansi());
}

Parsing and Validation

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Config: u8 {
        const DEBUG = 1 << 0;
        const VERBOSE = 1 << 1;
        const TRACE = 1 << 2;
        const PROFILE = 1 << 3;
    }
}
 
fn parse_config(value: u8) -> Option<Config> {
    Config::from_bits(value)
}
 
fn parse_config_truncate(value: u8) -> Config {
    Config::from_bits_truncate(value)
}
 
fn parse_config_strict(value: u8) -> Result<Config, String> {
    Config::from_bits(value)
        .ok_or_else(|| format!("Invalid config bits: {:b}", value))
}
 
fn main() {
    // Valid bits
    let valid = 0b0011;
    match parse_config(valid) {
        Some(config) => println!("Parsed: {:?}", config),
        None => println!("Invalid"),
    }
    
    // Invalid bits (includes bit 4 which isn't defined)
    let invalid = 0b10000;
    println!("\nParsing {} with different methods:", invalid);
    
    // from_bits returns None for invalid
    println!("from_bits: {:?}", parse_config(invalid));
    
    // from_bits_truncate removes invalid bits
    let truncated = parse_config_truncate(invalid);
    println!("from_bits_truncate: {:?}", truncated);
    
    // from_bits_retain keeps all bits
    let retained = Config::from_bits_retain(invalid);
    println!("from_bits_retain: {:?}", retained);
}

Iterating Over Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Weekday: u8 {
        const MONDAY = 1 << 0;
        const TUESDAY = 1 << 1;
        const WEDNESDAY = 1 << 2;
        const THURSDAY = 1 << 3;
        const FRIDAY = 1 << 4;
        const SATURDAY = 1 << 5;
        const SUNDAY = 1 << 6;
    }
}
 
fn main() {
    let workdays = Weekday::MONDAY | Weekday::TUESDAY | Weekday::WEDNESDAY 
        | Weekday::THURSDAY | Weekday::FRIDAY;
    
    // Iterate over set flags
    println!("Workdays:");
    for day in workdays.iter() {
        println!("  {:?}", day);
    }
    
    // Count flags
    println!("\nNumber of workdays: {}", workdays.iter().count());
    
    // Check specific days
    let weekend = Weekday::SATURDAY | Weekday::SUNDAY;
    println!("\nIntersects weekend: {}", workdays.intersects(weekend));
    
    // Full schedule
    let full_week = workdays | weekend;
    println!("\nAll days:");
    for day in full_week.iter() {
        println!("  {:?}", day);
    }
}

Serialization with Serde

// Requires: bitflags = { version = "2.0", features = ["serde"] }
 
use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    struct Permissions: u32 {
        const READ = 1;
        const WRITE = 2;
        const DELETE = 4;
        const ADMIN = 8;
    }
}
 
fn main() {
    let perms = Permissions::READ | Permissions::WRITE;
    
    // Serialize to JSON
    let json = serde_json::to_string(&perms).unwrap();
    println!("JSON: {}", json);
    
    // Deserialize from JSON
    let parsed: Permissions = serde_json::from_str(&json).unwrap();
    println!("Parsed: {:?}", parsed);
    
    assert_eq!(perms, parsed);
}

Database Storage

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct UserFlags: u32 {
        const EMAIL_VERIFIED = 1 << 0;
        const PHONE_VERIFIED = 1 << 1;
        const TWO_FACTOR_ENABLED = 1 << 2;
        const PREMIUM = 1 << 3;
        const MODERATOR = 1 << 4;
        const ADMIN = 1 << 5;
        const BANNED = 1 << 6;
        const SUSPENDED = 1 << 7;
    }
}
 
struct User {
    id: u64,
    username: String,
    flags: UserFlags,
}
 
impl User {
    fn new(id: u64, username: &str) -> Self {
        Self {
            id,
            username: username.to_string(),
            flags: UserFlags::empty(),
        }
    }
    
    fn is_admin(&self) -> bool {
        self.flags.contains(UserFlags::ADMIN)
    }
    
    fn is_banned(&self) -> bool {
        self.flags.contains(UserFlags::BANNED)
    }
    
    fn can_moderate(&self) -> bool {
        self.flags.intersects(UserFlags::ADMIN | UserFlags::MODERATOR)
    }
    
    fn is_premium(&self) -> bool {
        self.flags.contains(UserFlags::PREMIUM)
    }
    
    fn set_flag(&mut self, flag: UserFlags, value: bool) {
        self.flags.set(flag, value);
    }
    
    // Convert to database storage
    fn to_db_flags(&self) -> u32 {
        self.flags.bits()
    }
    
    // Load from database
    fn from_db_flags(id: u64, username: &str, db_flags: u32) -> Self {
        Self {
            id,
            username: username.to_string(),
            flags: UserFlags::from_bits_truncate(db_flags),
        }
    }
}
 
fn main() {
    let mut user = User::new(1, "alice");
    
    // Set flags
    user.set_flag(UserFlags::EMAIL_VERIFIED, true);
    user.set_flag(UserFlags::PREMIUM, true);
    
    println!("User: {}", user.username);
    println!("Is premium: {}", user.is_premium());
    println!("Is admin: {}", user.is_admin());
    
    // Store to database
    let db_value = user.to_db_flags();
    println!("\nDatabase value: {}", db_value);
    
    // Load from database
    let loaded = User::from_db_flags(1, "alice", db_value);
    println!("Loaded flags: {:?}", loaded.flags);
}

Network Protocol Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct TcpFlags: u8 {
        const FIN = 0x01;
        const SYN = 0x02;
        const RST = 0x04;
        const PSH = 0x08;
        const ACK = 0x10;
        const URG = 0x20;
        const ECE = 0x40;
        const CWR = 0x80;
    }
}
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct IpFlags: u16 {
        const RESERVED = 0x8000;
        const DONT_FRAGMENT = 0x4000;
        const MORE_FRAGMENTS = 0x2000;
    }
}
 
fn analyze_tcp_packet(flags: TcpFlags) {
    println!("TCP Flags: {:?}", flags);
    
    if flags.contains(TcpFlags::SYN) && !flags.contains(TcpFlags::ACK) {
        println!("  -> SYN packet (connection initiation)");
    }
    
    if flags.contains(TcpFlags::SYN) && flags.contains(TcpFlags::ACK) {
        println!("  -> SYN-ACK packet (connection response)");
    }
    
    if flags.contains(TcpFlags::FIN) {
        println!("  -> FIN packet (connection close)");
    }
    
    if flags.contains(TcpFlags::RST) {
        println!("  -> RST packet (connection reset)");
    }
    
    if flags.contains(TcpFlags::PSH) {
        println!("  -> Push flag set (push data to app)");
    }
}
 
fn main() {
    // SYN packet
    let syn = TcpFlags::SYN;
    analyze_tcp_packet(syn);
    
    // SYN-ACK packet
    let syn_ack = TcpFlags::SYN | TcpFlags::ACK;
    println!();
    analyze_tcp_packet(syn_ack);
    
    // FIN-ACK packet
    let fin_ack = TcpFlags::FIN | TcpFlags::ACK;
    println!();
    analyze_tcp_packet(fin_ack);
    
    // RST packet
    let rst = TcpFlags::RST;
    println!();
    analyze_tcp_packet(rst);
}

Comparing Flag Values

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
    struct Level: u8 {
        const NONE = 0;
        const LOW = 1;
        const MEDIUM = 2;
        const HIGH = 4;
        const CRITICAL = 8;
    }
}
 
fn main() {
    let a = Level::LOW | Level::MEDIUM;
    let b = Level::MEDIUM;
    let c = Level::HIGH | Level::CRITICAL;
    
    // Equality
    println!("a == b: {}", a == b);
    println!("a != b: {}", a != b);
    
    // Subset/superset checks
    let ab = Level::LOW | Level::MEDIUM;
    println!("\n{:?} contains {:?}: {}", a, Level::LOW, a.contains(Level::LOW));
    println!("{:?} contains {:?}: {}", a, Level::HIGH, a.contains(Level::HIGH));
    
    // Comparison (based on bits)
    println!("\n{:?} < {:?}: {}", b, a, b.bits() < a.bits());
    println!("{:?} < {:?}: {}", a, c, a.bits() < c.bits());
}

Real-World Example: Entity Component System

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    struct ComponentFlags: u32 {
        const POSITION = 1 << 0;
        const VELOCITY = 1 << 1;
        const RENDER = 1 << 2;
        const PHYSICS = 1 << 3;
        const AI = 1 << 4;
        const HEALTH = 1 << 5;
        const INPUT = 1 << 6;
        const ANIMATION = 1 << 7;
    }
}
 
struct Entity {
    id: u64,
    components: ComponentFlags,
}
 
impl Entity {
    fn new(id: u64) -> Self {
        Self {
            id,
            components: ComponentFlags::empty(),
        }
    }
    
    fn add_component(&mut self, component: ComponentFlags) {
        self.components.insert(component);
    }
    
    fn has_component(&self, component: ComponentFlags) -> bool {
        self.components.contains(component)
    }
    
    fn has_all(&self, components: ComponentFlags) -> bool {
        self.components.contains(components)
    }
    
    fn has_any(&self, components: ComponentFlags) -> bool {
        self.components.intersects(components)
    }
}
 
struct System {
    name: String,
    required: ComponentFlags,
}
 
impl System {
    fn new(name: &str, required: ComponentFlags) -> Self {
        Self {
            name: name.to_string(),
            required,
        }
    }
    
    fn can_process(&self, entity: &Entity) -> bool {
        entity.has_all(self.required)
    }
}
 
fn main() {
    let mut player = Entity::new(1);
    player.add_component(ComponentFlags::POSITION);
    player.add_component(ComponentFlags::VELOCITY);
    player.add_component(ComponentFlags::RENDER);
    player.add_component(ComponentFlags::INPUT);
    player.add_component(ComponentFlags::HEALTH);
    
    let mut enemy = Entity::new(2);
    enemy.add_component(ComponentFlags::POSITION);
    enemy.add_component(ComponentFlags::VELOCITY);
    enemy.add_component(ComponentFlags::RENDER);
    enemy.add_component(ComponentFlags::AI);
    enemy.add_component(ComponentFlags::HEALTH);
    
    let mut static_prop = Entity::new(3);
    static_prop.add_component(ComponentFlags::POSITION);
    static_prop.add_component(ComponentFlags::RENDER);
    
    // Define systems
    let movement_system = System::new("Movement", 
        ComponentFlags::POSITION | ComponentFlags::VELOCITY);
    let render_system = System::new("Render", 
        ComponentFlags::POSITION | ComponentFlags::RENDER);
    let input_system = System::new("Input", 
        ComponentFlags::POSITION | ComponentFlags::INPUT);
    let ai_system = System::new("AI", 
        ComponentFlags::POSITION | ComponentFlags::AI);
    
    let entities = vec![&player, &enemy, &static_prop];
    
    println!("Movement system processes:");
    for entity in &entities {
        if movement_system.can_process(entity) {
            println!("  Entity {}", entity.id);
        }
    }
    
    println!("\nInput system processes:");
    for entity in &entities {
        if input_system.can_process(entity) {
            println!("  Entity {}", entity.id);
        }
    }
}

Empty, All, and Undefined Bits

use bitflags::bitflags;
 
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    struct Flags: u8 {
        const A = 0b0001;
        const B = 0b0010;
        const C = 0b0100;
    }
}
 
fn main() {
    // Empty flags
    let empty = Flags::empty();
    println!("Empty: {:?}", empty);
    println!("Is empty: {}", empty.is_empty());
    println!("Bits: {:b}", empty.bits());
    
    // All defined flags
    let all = Flags::all();
    println!("\nAll: {:?}", all);
    println!("Is all: {}", all.is_all());
    println!("Bits: {:b}", all.bits());
    
    // Undefined bits
    let undefined = 0b1000; // Bit 3 is not defined
    
    // from_bits returns None for undefined bits
    println!("\nfrom_bits(0b1000): {:?}", Flags::from_bits(undefined));
    
    // from_bits_truncate removes undefined bits
    println!("from_bits_truncate(0b1000): {:?}", Flags::from_bits_truncate(undefined));
    
    // from_bits_retain keeps undefined bits
    let retained = Flags::from_bits_retain(undefined);
    println!("from_bits_retain(0b1000): {:?}", retained);
    println!("Retained bits: {:b}", retained.bits());
    
    // Check for undefined bits
    println!("\nRetained is empty: {}", retained.is_empty());
    println!("Retained is all: {}", retained.is_all());
}

Summary

  • Use bitflags! macro to define flag types backed by integers
  • Each flag should be a unique bit: 1 << n or explicit hex/octal/binary values
  • Combine flags with |, intersect with &, toggle with ^, complement with !
  • contains(flag) checks if a flag is set
  • insert(flag), remove(flag), toggle(flag) modify flags in place
  • empty() returns no flags, all() returns all defined flags
  • from_bits() returns Option<Flags>None if undefined bits present
  • from_bits_truncate() removes undefined bits
  • from_bits_retain() keeps all bits including undefined ones
  • iter() yields each set flag individually
  • Add #[derive(Debug, Clone, Copy, PartialEq, Eq)] for common traits
  • Enable serde feature for JSON/binary serialization
  • Perfect for: permissions, options, capabilities, state flags, protocol flags
  • Use bits() to get raw integer value for storage/transmission