How does bitflags::Flags::contains enable checking multiple flags at once?

The bitflags::Flags::contains method tests whether a flags value includes all specified flags, enabling efficient multi-flag checks through a single bitwise operation. Unlike checking each flag individually with multiple method calls, contains performs a bitwise AND between the value and the flags being tested, returning true only if all specified bits are set. This pattern is fundamental to bitflags design—flags are combined through bitwise OR and tested through bitwise AND, making multi-flag operations both expressive and performant.

Basic contains Usage

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug)]
    struct Permissions: u32 {
        const READ = 0b001;
        const WRITE = 0b010;
        const EXECUTE = 0b100;
    }
}
 
fn basic_contains() {
    let perms = Permissions::READ | Permissions::WRITE;
    
    // Check for single flag
    assert!(perms.contains(Permissions::READ));
    
    // Check for multiple flags at once
    assert!(perms.contains(Permissions::READ | Permissions::WRITE));
    
    // Fails if any flag is missing
    assert!(!perms.contains(Permissions::EXECUTE));
    assert!(!perms.contains(Permissions::READ | Permissions::EXECUTE));
}

contains returns true only if all specified flags are present in the value.

Bitwise Implementation

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Flags: u8 {
        const A = 0b0001;
        const B = 0b0010;
        const C = 0b0100;
        const D = 0b1000;
    }
}
 
fn bitwise_explanation() {
    let value = Flags::A | Flags::B | Flags::C; // 0b0111
    let test = Flags::A | Flags::B;              // 0b0011
    
    // contains performs: (value & test) == test
    // value & test = 0b0111 & 0b0011 = 0b0011
    // 0b0011 == 0b0011 -> true
    assert!(value.contains(test));
    
    let test2 = Flags::A | Flags::D;             // 0b1001
    // value & test2 = 0b0111 & 0b1001 = 0b0001
    // 0b0001 != 0b1001 -> false
    assert!(!value.contains(test2));
}

The implementation uses (self.bits & other.bits) == other.bits for efficiency.

Multiple Flag Combinations

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct FilePerms: u32 {
        const READ = 0b001;
        const WRITE = 0b010;
        const EXECUTE = 0b100;
        const RW = Self::READ.bits | Self::WRITE.bits;
        const RWX = Self::READ.bits | Self::WRITE.bits | Self::EXECUTE.bits;
    }
}
 
fn composite_flags() {
    let file = FilePerms::RW;
    
    // Can check against composite flags
    assert!(file.contains(FilePerms::RW));
    assert!(file.contains(FilePerms::READ | FilePerms::WRITE));
    
    // Both are equivalent
    assert!(file.contains(FilePerms::READ));
    assert!(file.contains(FilePerms::WRITE));
    assert!(!file.contains(FilePerms::EXECUTE));
}

Composite flags combine multiple flags for convenient checking.

Checking Multiple Conditions

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Capabilities: u32 {
        const CAN_READ = 0b0001;
        const CAN_WRITE = 0b0010;
        const CAN_DELETE = 0b0100;
        const CAN_ADMIN = 0b1000;
    }
}
 
fn check_multiple() {
    let user_caps = Capabilities::CAN_READ | Capabilities::CAN_WRITE;
    
    // Single operation to check multiple flags
    if user_caps.contains(Capabilities::CAN_READ | Capabilities::CAN_WRITE) {
        println!("User can read and write");
    }
    
    // Compare with individual checks
    if user_caps.contains(Capabilities::CAN_READ) 
        && user_caps.contains(Capabilities::CAN_WRITE) 
    {
        println!("User can read and write (verbose)");
    }
}

contains with multiple flags is more concise than multiple individual calls.

Permission Validation Example

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug)]
    struct Permission: u32 {
        const VIEW = 0b0001;
        const EDIT = 0b0010;
        const DELETE = 0b0100;
        const SHARE = 0b1000;
        const ADMIN = Self::VIEW.bits | Self::EDIT.bits 
                    | Self::DELETE.bits | Self::SHARE.bits;
    }
}
 
struct User {
    name: String,
    permissions: Permission,
}
 
fn check_permission(user: &User, required: Permission) -> bool {
    user.permissions.contains(required)
}
 
fn access_document(user: &User) -> Result<String, &'static str> {
    // Require both VIEW and EDIT
    if !check_permission(user, Permission::VIEW | Permission::EDIT) {
        return Err("Insufficient permissions: need VIEW and EDIT");
    }
    
    Ok("Access granted".to_string())
}
 
fn delete_document(user: &User) -> Result<String, &'static str> {
    // Require DELETE permission
    if !check_permission(user, Permission::DELETE) {
        return Err("Insufficient permissions: need DELETE");
    }
    
    Ok("Document deleted".to_string())
}

Permission checks using contains are readable and efficient.

Intersection with intersects

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Status: u32 {
        const ACTIVE = 0b0001;
        const PENDING = 0b0010;
        const VERIFIED = 0b0100;
        const SUSPENDED = 0b1000;
    }
}
 
fn contains_vs_intersects() {
    let status = Status::ACTIVE | Status::VERIFIED;
    
    // contains: ALL flags must be present
    assert!(status.contains(Status::ACTIVE));
    assert!(status.contains(Status::ACTIVE | Status::VERIFIED));
    assert!(!status.contains(Status::ACTIVE | Status::PENDING));
    
    // intersects: ANY flag can be present
    assert!(status.intersects(Status::ACTIVE));
    assert!(status.intersects(Status::ACTIVE | Status::VERIFIED));
    assert!(status.intersects(Status::ACTIVE | Status::PENDING)); // true because ACTIVE matches
    assert!(!status.intersects(Status::PENDING | Status::SUSPENDED)); // neither present
}

contains requires all flags; intersects requires any flag.

Empty and All Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Feature: u32 {
        const FEATURE_A = 0b0001;
        const FEATURE_B = 0b0010;
        const FEATURE_C = 0b0100;
    }
}
 
fn empty_and_all() {
    // Empty flags
    let empty = Feature::empty();
    assert!(!empty.contains(Feature::FEATURE_A));
    assert!(empty.contains(Feature::empty())); // Empty contains empty
    
    // All flags
    let all = Feature::all();
    assert!(all.contains(Feature::FEATURE_A));
    assert!(all.contains(Feature::FEATURE_A | Feature::FEATURE_B));
    assert!(all.contains(Feature::all())); // All contains all
}

empty() contains nothing; all() contains all defined flags.

Inserting and Removing Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug)]
    struct Mode: u32 {
        const READ = 0b001;
        const WRITE = 0b010;
        const EXECUTE = 0b100;
    }
}
 
fn modify_flags() {
    let mut mode = Mode::READ;
    
    // Insert adds flags
    mode.insert(Mode::WRITE);
    assert!(mode.contains(Mode::READ | Mode::WRITE));
    
    // Remove clears flags
    mode.remove(Mode::READ);
    assert!(!mode.contains(Mode::READ));
    assert!(mode.contains(Mode::WRITE));
    
    // Toggle flips flags
    mode.toggle(Mode::READ | Mode::EXECUTE);
    assert!(mode.contains(Mode::READ));
    assert!(mode.contains(Mode::WRITE));
    assert!(mode.contains(Mode::EXECUTE));
}

Flag modifications can affect multiple flags at once.

Set Operation Comparison

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Options: u32 {
        const A = 0b0001;
        const B = 0b0010;
        const C = 0b0100;
    }
}
 
fn set_operations() {
    let set1 = Options::A | Options::B;
    let set2 = Options::B | Options::C;
    
    // Union (OR): contains either set
    let union = set1 | set2;
    assert!(union.contains(Options::A));
    assert!(union.contains(Options::B));
    assert!(union.contains(Options::C));
    
    // Intersection (AND): contains both sets
    let intersection = set1 & set2;
    assert!(intersection.contains(Options::B));
    assert!(!intersection.contains(Options::A));
    
    // Difference: contains set1 but not set2
    let difference = set1 - set2;
    assert!(difference.contains(Options::A));
    assert!(!difference.contains(Options::B));
    
    // Symmetric difference (XOR): contains exclusive flags
    let sym_diff = set1 ^ set2;
    assert!(sym_diff.contains(Options::A));
    assert!(sym_diff.contains(Options::C));
    assert!(!sym_diff.contains(Options::B));
}

Set operations manipulate flag combinations efficiently.

Complex Flag Validation

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Config: u32 {
        const ENABLED = 0b0001;
        const DEBUG = 0b0010;
        const VERBOSE = 0b0100;
        const SECURE = 0b1000;
    }
}
 
fn validate_config(config: Config) -> Result<(), String> {
    // If ENABLED, must have SECURE or not have DEBUG
    if config.contains(Config::ENABLED) {
        if config.contains(Config::DEBUG) && !config.contains(Config::SECURE) {
            return Err("DEBUG requires SECURE when ENABLED".to_string());
        }
    }
    
    // VERBOSE requires DEBUG
    if config.contains(Config::VERBOSE) && !config.contains(Config::DEBUG) {
        return Err("VERBOSE requires DEBUG".to_string());
    }
    
    Ok(())
}
 
fn test_validation() {
    let valid = Config::ENABLED | Config::SECURE | Config::DEBUG;
    assert!(validate_config(valid).is_ok());
    
    let invalid = Config::ENABLED | Config::DEBUG; // Missing SECURE
    assert!(validate_config(invalid).is_err());
}

contains enables complex validation logic with readable code.

Multiple Flag Checks Performance

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct Flags: u64 {
        const FLAG_1 = 1;
        const FLAG_2 = 2;
        const FLAG_3 = 4;
        const FLAG_4 = 8;
        const FLAG_5 = 16;
    }
}
 
fn performance_comparison() {
    let flags = Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3;
    let test = Flags::FLAG_1 | Flags::FLAG_2;
    
    // Single contains call - one bitwise AND + comparison
    let result = flags.contains(test);
    
    // Multiple individual checks - multiple AND operations + branches
    let result2 = flags.contains(Flags::FLAG_1) 
        && flags.contains(Flags::FLAG_2);
    
    // Both produce same result, but contains(test) is more efficient
    assert_eq!(result, result2);
}

A single contains with combined flags is more efficient than multiple calls.

Unset Checking with ! (Complement)

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy)]
    struct State: u32 {
        const INITIALIZED = 0b001;
        const CONNECTED = 0b010;
        const AUTHENTICATED = 0b100;
    }
}
 
fn check_not_set() {
    let state = State::INITIALIZED | State::CONNECTED;
    
    // Check what's NOT set using complement
    let not_authenticated = !State::AUTHENTICATED;
    let missing = state & not_authenticated;
    
    // missing.contains(AUTHENTICATED) is false - AUTHENTICATED not set
    assert!(!missing.contains(State::AUTHENTICATED));
    
    // Or simply check absence:
    assert!(!state.contains(State::AUTHENTICATED));
    
    // Check if exactly these flags are set
    if state == State::INITIALIZED | State::CONNECTED {
        println!("Exactly initialized and connected");
    }
}

Negation and equality checks complement contains for set operations.

Real-World Example: File System Permissions

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug)]
    struct FileMode: u32 {
        const USER_READ = 0o400;
        const USER_WRITE = 0o200;
        const USER_EXEC = 0o100;
        const GROUP_READ = 0o040;
        const GROUP_WRITE = 0o020;
        const GROUP_EXEC = 0o010;
        const OTHER_READ = 0o004;
        const OTHER_WRITE = 0o002;
        const OTHER_EXEC = 0o001;
        
        const USER_RW = Self::USER_READ.bits | Self::USER_WRITE.bits;
        const USER_RWX = Self::USER_RW.bits | Self::USER_EXEC.bits;
    }
}
 
fn check_file_mode() {
    // Typical file: rw-r--r--
    let file_mode = FileMode::USER_RW | FileMode::GROUP_READ | FileMode::OTHER_READ;
    
    // Check user has read and write
    assert!(file_mode.contains(FileMode::USER_READ | FileMode::USER_WRITE));
    
    // Check user does not have execute
    assert!(!file_mode.contains(FileMode::USER_EXEC));
    
    // Check group has read
    assert!(file_mode.contains(FileMode::GROUP_READ));
    
    // Check group does not have write
    assert!(!file_mode.contains(FileMode::GROUP_WRITE));
}

File permissions map naturally to bitflags with contains for checks.

Real-World Example: Protocol Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug)]
    struct TcpFlags: u16 {
        const FIN = 0x01;
        const SYN = 0x02;
        const RST = 0x04;
        const PSH = 0x08;
        const ACK = 0x10;
        const URG = 0x20;
    }
}
 
fn analyze_tcp_packet(flags: TcpFlags) -> &'static str {
    // Check for connection establishment (SYN + ACK)
    if flags.contains(TcpFlags::SYN | TcpFlags::ACK) {
        return "Connection established (SYN-ACK)";
    }
    
    // Check for connection initiation (SYN only, no ACK)
    if flags.contains(TcpFlags::SYN) && !flags.contains(TcpFlags::ACK) {
        return "Connection initiation (SYN)";
    }
    
    // Check for connection termination (FIN)
    if flags.contains(TcpFlags::FIN) {
        return "Connection closing (FIN)";
    }
    
    // Check for reset
    if flags.contains(TcpFlags::RST) {
        return "Connection reset";
    }
    
    // Check for data push with acknowledgment
    if flags.contains(TcpFlags::PSH | TcpFlags::ACK) {
        return "Data transmission";
    }
    
    "Unknown flag combination"
}

Protocol flags with multiple bits set are checked efficiently with contains.

Real-World Example: Feature Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug)]
    struct FeatureFlags: u64 {
        const DARK_MODE = 1 << 0;
        const NOTIFICATIONS = 1 << 1;
        const ANALYTICS = 1 << 2;
        const EXPERIMENTAL_UI = 1 << 3;
        const BETA_FEATURES = 1 << 4;
        
        const UI_EXPERIMENTS = Self::DARK_MODE.bits | Self::EXPERIMENTAL_UI.bits;
        const PRIVACY_SENSITIVE = Self::ANALYTICS.bits | Self::NOTIFICATIONS.bits;
    }
}
 
struct User {
    id: u64,
    enabled_features: FeatureFlags,
}
 
impl User {
    fn can_access(&self, required: FeatureFlags) -> bool {
        self.enabled_features.contains(required)
    }
}
 
fn feature_checks() {
    let user = User {
        id: 1,
        enabled_features: FeatureFlags::DARK_MODE | FeatureFlags::NOTIFICATIONS,
    };
    
    // Check single feature
    if user.can_access(FeatureFlags::DARK_MODE) {
        println!("Dark mode available");
    }
    
    // Check multiple features at once
    if user.can_access(FeatureFlags::UI_EXPERIMENTS) {
        println!("UI experiments available");
    } else {
        println!("Missing some UI features");
    }
    
    // Check privacy-sensitive features
    if user.can_access(FeatureFlags::PRIVACY_SENSITIVE) {
        println!("All privacy features enabled");
    }
}

Feature flag combinations are checked with single contains calls.

Synthesis

Key behaviors:

Method Condition Use Case
contains(FLAG) All bits set Check for specific flags
contains(A | B) Both A and B set Check multiple flags
intersects(FLAG) Any bit set Check for any match
== FLAG Exactly this value Check exact match

Implementation details:

Operation Bitwise equivalent
contains(other) (self & other) == other
intersects(other) (self & other) != 0
insert(other) self | other
remove(other) self & !other

Common patterns:

Pattern Code Meaning
Has all flags.contains(A | B) Both A and B present
Has any flags.intersects(A | B) At least one present
Has none !flags.intersects(A | B) Neither present
Has exactly flags == A | B Only A and B, nothing else

Key insight: bitflags::Flags::contains enables checking multiple flags through a single efficient bitwise operation—(value & test) == test returns true only if all tested bits are set in the value. This is more efficient and readable than multiple individual checks because it performs one AND and one comparison instead of multiple operations. Combined with bitwise OR (|), contains expresses "has all of these flags" naturally, while intersects expresses "has any of these flags." The pattern extends to flag modification: insert, remove, and toggle all accept combined flags for batch operations. This makes bitflags ideal for permission systems, protocol headers, feature flags, and state machines where multiple boolean states combine efficiently into a single integer.