What is the purpose of bitflags::Flags::intersection for combining flag values with AND semantics?

bitflags::Flags::intersection computes the bitwise AND of two flag values, returning a new flags value containing only the bits that are set in both operands, which is essential for finding common capabilities, checking permissions, and filtering flags to those that overlap between two sets. Unlike union (OR) which combines flags, intersection (AND) restricts flags to the subset present in both values, enabling precise queries about flag overlap and permission intersections.

Defining Flags with bitflags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Permissions: u32 {
        const READ = 0b0001;
        const WRITE = 0b0010;
        const EXECUTE = 0b0100;
        const ADMIN = 0b1000;
    }
}
 
fn basic_flags() {
    // Single flag
    let read = Permissions::READ;
    
    // Combined flags (union/OR)
    let read_write = Permissions::READ | Permissions::WRITE;
    
    // All flags
    let all = Permissions::all();
    
    // No flags
    let none = Permissions::empty();
    
    println!("READ: {:?} = {:b}", read, read.bits());
    println!("READ | WRITE: {:?} = {:b}", read_write, read_write.bits());
}

The bitflags! macro creates typesafe bitwise flag containers.

Basic Intersection Operation

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Permissions: u32 {
        const READ = 0b0001;
        const WRITE = 0b0010;
        const EXECUTE = 0b0100;
        const ADMIN = 0b1000;
    }
}
 
fn basic_intersection() {
    let read_write = Permissions::READ | Permissions::WRITE;
    let write_exec = Permissions::WRITE | Permissions::EXECUTE;
    
    // intersection() returns flags present in BOTH sets
    let common = read_write.intersection(write_exec);
    
    // Result: Only WRITE is in both
    assert_eq!(common, Permissions::WRITE);
    
    // Using the & operator (shorthand for intersection)
    let common_op = read_write & write_exec;
    assert_eq!(common_op, Permissions::WRITE);
    
    // Binary representation:
    // read_write:  0011 (READ | WRITE)
    // write_exec:  0110 (WRITE | EXECUTE)
    // intersection: 0010 (WRITE only - bit is 1 in BOTH)
}

intersection returns only bits set in both operands.

Intersection vs Union

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Caps: u8 {
        const A = 0b0001;
        const B = 0b0010;
        const C = 0b0100;
        const D = 0b1000;
    }
}
 
fn intersection_vs_union() {
    let set1 = Caps::A | Caps::B | Caps::C;  // 0111
    let set2 = Caps::B | Caps::C | Caps::D;  // 1110
    
    // Union (OR): All flags from either set
    let union = set1.union(set2);
    // 0111 | 1110 = 1111 (A | B | C | D)
    assert_eq!(union, Caps::A | Caps::B | Caps::C | Caps::D);
    
    // Intersection (AND): Only flags in both sets
    let intersect = set1.intersection(set2);
    // 0111 & 1110 = 0110 (B | C)
    assert_eq!(intersect, Caps::B | Caps::C);
    
    // Difference: Flags in set1 but not in set2
    let diff = set1.difference(set2);
    // 0111 & !1110 = 0111 & 0001 = 0001 (A)
    assert_eq!(diff, Caps::A);
    
    // Symmetric difference: Flags in either but not both
    let sym_diff = set1.symmetric_difference(set2);
    // 0111 ^ 1110 = 1001 (A | D)
    assert_eq!(sym_diff, Caps::A | Caps::D);
}

Union combines; intersection finds overlap.

Operator Syntax

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Flags: u8 {
        const X = 0b01;
        const Y = 0b10;
    }
}
 
fn operator_syntax() {
    let a = Flags::X;
    let b = Flags::Y;
    let both = Flags::X | Flags::Y;
    
    // Method syntax
    let inter1 = a.intersection(both);  // X
    
    // Operator syntax (&)
    let inter2 = a & both;  // X
    
    // &= for in-place intersection
    let mut flags = Flags::X | Flags::Y;
    flags &= Flags::X;  // Now just X
    
    assert_eq!(inter1, Flags::X);
    assert_eq!(inter2, Flags::X);
    assert_eq!(flags, Flags::X);
}

The & operator is the ergonomic form of intersection().

Checking for Overlapping Flags

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Permissions: u32 {
        const READ = 0b0001;
        const WRITE = 0b0010;
        const EXECUTE = 0b0100;
        const ADMIN = 0b1000;
    }
}
 
fn checking_overlap() {
    let user_perms = Permissions::READ | Permissions::WRITE;
    let required = Permissions::WRITE | Permissions::EXECUTE;
    
    // Check if ANY required permission is present
    // Use intersection to find common flags
    let overlap = user_perms.intersection(required);
    
    if overlap.contains(Permissions::WRITE) {
        println!("User has WRITE permission");
    }
    
    if overlap.contains(Permissions::EXECUTE) {
        println!("User has EXECUTE permission");
    } else {
        println!("User lacks EXECUTE permission");
    }
    
    // Check if ALL required are present
    // This is different: use contains() or intersection equals required
    if user_perms.contains(Permissions::WRITE) && user_perms.contains(Permissions::EXECUTE) {
        println!("User has all required permissions");
    } else {
        println!("User lacks some required permissions");
    }
    
    // Using intersection to check "has any of these"
    let has_any_required = !user_perms.intersection(required).is_empty();
    println!("User has at least one required permission: {}", has_any_required);
}

Intersection reveals which flags from a query are present.

Permission Intersection Example

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Access: u32 {
        const READ = 0b0001;
        const WRITE = 0b0010;
        const DELETE = 0b0100;
        const SHARE = 0b1000;
    }
}
 
struct User {
    name: String,
    access: Access,
}
 
struct Resource {
    name: String,
    required_access: Access,
}
 
fn can_access(user: &User, resource: &Resource) -> bool {
    // Find what access user has that resource requires
    let common_access = user.access.intersection(resource.required_access);
    
    // If intersection equals required, user has ALL required access
    // If intersection is empty, user has NONE of the required access
    // If intersection is partial, user has SOME required access
    
    // For "has all required":
    common_access == resource.required_access
}
 
fn permission_example() {
    let user = User {
        name: "alice".to_string(),
        access: Access::READ | Access::WRITE | Access::SHARE,
    };
    
    let document = Resource {
        name: "report.pdf".to_string(),
        required_access: Access::READ | Access::WRITE,
    };
    
    // User has READ | WRITE, resource requires READ | WRITE
    // Intersection: READ | WRITE == required_access
    assert!(can_access(&user, &document));
    
    let shared_doc = Resource {
        name: "shared.pdf".to_string(),
        required_access: Access::READ | Access::SHARE,
    };
    
    // User has READ | SHARE, resource requires READ | SHARE
    // Intersection: READ | SHARE == required_access
    assert!(can_access(&user, &shared_doc));
    
    let admin_doc = Resource {
        name: "admin.pdf".to_string(),
        required_access: Access::DELETE,
    };
    
    // User doesn't have DELETE
    // Intersection: empty != DELETE
    assert!(!can_access(&user, &admin_doc));
}

Intersection can validate if all required permissions are present.

Intersection for Filtering

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Features: u32 {
        const FAST = 0b0001;
        const SECURE = 0b0010;
        const COMPATIBLE = 0b0100;
        const EXPERIMENTAL = 0b1000;
    }
}
 
fn filter_features() {
    let supported = Features::FAST | Features::SECURE | Features::COMPATIBLE;
    let requested = Features::FAST | Features::SECURE | Features::EXPERIMENTAL;
    
    // What features are both supported AND requested?
    let available = supported.intersection(requested);
    
    // Result: FAST | SECURE
    // (EXPERIMENTAL is requested but not supported)
    assert_eq!(available, Features::FAST | Features::SECURE);
    
    // What requested features are NOT supported?
    let unsupported = requested.difference(supported);
    assert_eq!(unsupported, Features::EXPERIMENTAL);
    
    // Using intersection to filter feature sets
    fn get_enabled_features(supported: Features, requested: Features) -> Features {
        // Only features that are both supported and requested
        supported.intersection(requested)
    }
    
    let enabled = get_enabled_features(supported, requested);
    assert!(enabled.contains(Features::FAST));
    assert!(enabled.contains(Features::SECURE));
    assert!(!enabled.contains(Features::EXPERIMENTAL));
}

Intersection filters to only those items present in both sets.

Empty Intersection (Disjoint Sets)

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Groups: u8 {
        const ADMIN = 0b0001;
        const MODERATOR = 0b0010;
        const USER = 0b0100;
        const GUEST = 0b1000;
    }
}
 
fn disjoint_sets() {
    let admins = Groups::ADMIN;
    let guests = Groups::GUEST;
    
    // No overlap
    let common = admins.intersection(guests);
    
    // Empty intersection means disjoint sets
    assert!(common.is_empty());
    assert!(!admins.intersects(guests));  // Helper method
    
    // Disjoint sets have no bits in common
    let can_both_do = admins.intersection(guests);
    // Empty - no shared capabilities
    
    // Compare with overlapping sets
    let moderators = Groups::MODERATOR;
    let users = Groups::USER | Groups::MODERATOR;  // Users can be moderators too
    
    let overlap = moderators.intersection(users);
    assert_eq!(overlap, Groups::MODERATOR);
    assert!(moderators.intersects(users));
}

Empty intersection indicates disjoint flag sets.

Computing Effective Permissions

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct FilePerms: u16 {
        const OWNER_READ = 0b0000001;
        const OWNER_WRITE = 0b0000010;
        const OWNER_EXEC = 0b0000100;
        const GROUP_READ = 0b0001000;
        const GROUP_WRITE = 0b0010000;
        const GROUP_EXEC = 0b0100000;
        const OTHER_READ = 0b1000000;
        const OTHER_WRITE = 0b10000000;
        const OTHER_EXEC = 0b100000000;
    }
}
 
struct File {
    name: String,
    permissions: FilePerms,
}
 
struct User {
    name: String,
    is_owner: bool,
    is_in_group: bool,
}
 
fn effective_permissions(user: &User, file: &File) -> FilePerms {
    // Determine which permission set applies
    let applicable_perms = if user.is_owner {
        FilePerms::OWNER_READ | FilePerms::OWNER_WRITE | FilePerms::OWNER_EXEC
    } else if user.is_in_group {
        FilePerms::GROUP_READ | FilePerms::GROUP_WRITE | FilePerms::GROUP_EXEC
    } else {
        FilePerms::OTHER_READ | FilePerms::OTHER_WRITE | FilePerms::OTHER_EXEC
    };
    
    // Intersection: only permissions that are both set AND applicable
    file.permissions.intersection(applicable_perms)
}
 
fn effective_perms_example() {
    let file = File {
        name: "script.sh".to_string(),
        permissions: FilePerms::OWNER_READ | FilePerms::OWNER_WRITE | FilePerms::OWNER_EXEC
                   | FilePerms::GROUP_READ | FilePerms::GROUP_EXEC
                   | FilePerms::OTHER_READ,
    };
    
    let owner = User { name: "owner".into(), is_owner: true, is_in_group: false };
    let group_member = User { name: "member".into(), is_owner: false, is_in_group: true };
    let other = User { name: "guest".into(), is_owner: false, is_in_group: false };
    
    // Owner gets: intersection with OWNER_* bits
    let owner_perms = effective_permissions(&owner, &file);
    assert_eq!(owner_perms, FilePerms::OWNER_READ | FilePerms::OWNER_WRITE | FilePerms::OWNER_EXEC);
    
    // Group member gets: intersection with GROUP_* bits
    let group_perms = effective_permissions(&group_member, &file);
    assert_eq!(group_perms, FilePerms::GROUP_READ | FilePerms::GROUP_EXEC);
    
    // Other gets: intersection with OTHER_* bits
    let other_perms = effective_permissions(&other, &file);
    assert_eq!(other_perms, FilePerms::OTHER_READ);
}

Intersection computes effective permissions from set bits and applicable bit masks.

Combining Intersection with Other Operations

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Options: u8 {
        const A = 0b0001;
        const B = 0b0010;
        const C = 0b0100;
        const D = 0b1000;
    }
}
 
fn combined_operations() {
    let set1 = Options::A | Options::B | Options::C;
    let set2 = Options::B | Options::C | Options::D;
    
    // Intersection followed by union
    let common = set1.intersection(set2);  // B | C
    let with_extra = common.union(Options::A);  // A | B | C
    assert_eq!(with_extra, Options::A | Options::B | Options::C);
    
    // Intersection followed by difference
    let overlap = set1.intersection(set2);  // B | C
    let removed = overlap.difference(Options::B);  // C
    assert_eq!(removed, Options::C);
    
    // Complement then intersection
    let not_d = Options::all().difference(Options::D);  // A | B | C
    let common_not_d = set1.intersection(not_d);  // A | B | C
    assert_eq!(common_not_d, set1);
    
    // Multiple intersections
    let set3 = Options::A | Options::B;
    let all_three = set1.intersection(set2).intersection(set3);
    // (A|B|C) & (B|C|D) & (A|B) = B
    assert_eq!(all_three, Options::B);
}

Intersection composes with other set operations.

Intersection Methods

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Flags: u8 {
        const X = 1;
        const Y = 2;
        const Z = 4;
    }
}
 
fn intersection_methods() {
    let a = Flags::X | Flags::Y;
    let b = Flags::Y | Flags::Z;
    
    // Primary method: intersection()
    let common = a.intersection(b);
    
    // Operator: & (bitwise AND)
    let common_op = a & b;
    
    // Assignment: &=
    let mut mutable = Flags::X | Flags::Y | Flags::Z;
    mutable &= Flags::Y | Flags::Z;  // X removed, Y|Z kept
    
    // intersects(): checks if intersection is non-empty
    assert!(a.intersects(b));  // They share Y
    assert!(!Flags::X.intersects(Flags::Z));  // Disjoint
    
    // contains(): checks if all bits in other are present
    assert!(a.contains(Flags::X));  // a has X
    assert!(!a.contains(Flags::Z));  // a doesn't have Z
    
    // set(): adds bits
    let mut added = a;
    added.set(Flags::Z, true);  // Now X|Y|Z
    
    // remove(): removes specific bits
    let mut removed = a;
    removed.remove(Flags::X);  // Now just Y
}

bitflags provides multiple methods for intersection operations.

Performance Characteristics

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Flags: u128 {  // Can use any integer type
        const A = 1;
        const B = 2;
    }
}
 
fn performance() {
    // intersection() is a single bitwise AND operation
    // Complexity: O(1) - single CPU instruction
    
    let a = Flags::A | Flags::B;
    let b = Flags::A;
    
    // This compiles to just:
    // AND instruction on the underlying integer
    let common = a.intersection(b);
    
    // No heap allocation (Flags is Copy)
    // No iteration over bits
    // Single CPU instruction for the AND
    
    // For flags backed by u8, u16, u32, u64, u128:
    // - Size: sizeof(backing_type)
    // - Intersection: 1 CPU instruction
    // - Memory: sizeof(backing_type)
    
    // This is extremely fast compared to:
    // - Hash sets (hash + lookup)
    // - B-tree sets (tree traversal)
    // - Vec membership (linear scan)
}

Intersection is a single CPU instruction—extremely fast.

Real-World Example: Capability Negotiation

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Capabilities: u32 {
        const COMPRESSION = 0b0001;
        const ENCRYPTION = 0b0010;
        const STREAMING = 0b0100;
        const CACHING = 0b1000;
        const AUTH = 0b10000;
        const LOGGING = 0b100000;
    }
}
 
struct Client {
    name: String,
    capabilities: Capabilities,
}
 
struct Server {
    name: String,
    capabilities: Capabilities,
}
 
fn negotiate_capabilities(client: &Client, server: &Server) -> Capabilities {
    // Find capabilities both support
    client.capabilities.intersection(server.capabilities)
}
 
fn capability_example() {
    let client = Client {
        name: "mobile-app".to_string(),
        capabilities: Capabilities::COMPRESSION | Capabilities::STREAMING | Capabilities::AUTH,
    };
    
    let server = Server {
        name: "backend".to_string(),
        capabilities: Capabilities::COMPRESSION | Capabilities::ENCRYPTION 
                    | Capabilities::AUTH | Capabilities::LOGGING,
    };
    
    // Negotiated: capabilities both have
    let negotiated = negotiate_capabilities(&client, &server);
    
    // COMPRESSION and AUTH are in both
    assert_eq!(negotiated, Capabilities::COMPRESSION | Capabilities::AUTH);
    
    // STREAMING is client-only, ENCRYPTION and LOGGING are server-only
    assert!(!negotiated.contains(Capabilities::STREAMING));
    assert!(!negotiated.contains(Capabilities::ENCRYPTION));
    
    // Both can use COMPRESSION and AUTH
    assert!(negotiated.contains(Capabilities::COMPRESSION));
    assert!(negotiated.contains(Capabilities::AUTH));
}

Intersection finds mutually supported capabilities in protocol negotiation.

Using Intersection for Validation

use bitflags::bitflags;
 
bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct ValidationFlags: u8 {
        const NAME_PRESENT = 0b0001;
        const EMAIL_VALID = 0b0010;
        const AGE_VALID = 0b0100;
        const TERMS_ACCEPTED = 0b1000;
    }
}
 
struct FormData {
    name: Option<String>,
    email: Option<String>,
    age: Option<u8>,
    terms_accepted: bool,
}
 
fn validate(data: &FormData) -> ValidationFlags {
    let mut flags = ValidationFlags::empty();
    
    if data.name.is_some() {
        flags |= ValidationFlags::NAME_PRESENT;
    }
    if data.email.as_ref().map_or(false, |e| e.contains('@')) {
        flags |= ValidationFlags::EMAIL_VALID;
    }
    if data.age.map_or(false, |a| a >= 18 && a <= 120) {
        flags |= ValidationFlags::AGE_VALID;
    }
    if data.terms_accepted {
        flags |= ValidationFlags::TERMS_ACCEPTED;
    }
    
    flags
}
 
fn validation_example() {
    let required = ValidationFlags::NAME_PRESENT | ValidationFlags::EMAIL_VALID 
                 | ValidationFlags::AGE_VALID | ValidationFlags::TERMS_ACCEPTED;
    
    let data = FormData {
        name: Some("Alice".to_string()),
        email: Some("invalid".to_string()),  // No @
        age: Some(25),
        terms_accepted: true,
    };
    
    let validated = validate(&data);
    
    // What validations passed AND were required?
    let passed_required = validated.intersection(required);
    
    // NAME_PRESENT: yes
    // EMAIL_VALID: no
    // AGE_VALID: yes
    // TERMS_ACCEPTED: yes
    
    assert_eq!(passed_required, ValidationFlags::NAME_PRESENT | ValidationFlags::AGE_VALID 
                                          | ValidationFlags::TERMS_ACCEPTED);
    
    // Check if all required passed
    if passed_required == required {
        println!("All validations passed!");
    } else {
        println!("Missing validations!");
        
        // What's missing?
        let missing = required.difference(validated);
        println!("Missing: {:?}", missing);  // EMAIL_VALID
    }
}

Intersection can compare actual state against required state.

Synthesis

Core purpose:

// intersection() computes bitwise AND of two flag values
let common = flags1.intersection(flags2);
 
// Equivalent to:
let common = flags1 & flags2;
 
// Result contains ONLY bits set in BOTH operands

Key behaviors:

Operation Result Meaning
a.intersection(b) a & b Bits in both
a.union(b) a | b Bits in either
a.difference(b) a & !b Bits in a but not b
a.symmetric_difference(b) a ^ b Bits in one but not both

Common patterns:

// Check for any overlap
if a.intersects(b) {
    // They share at least one flag
}
 
// Check for complete containment
if a.intersection(b) == b {
    // a contains all of b
}
 
// Find missing flags
let missing = required.difference(actual);
 
// Find common flags
let common = flags1.intersection(flags2);
 
// Check if disjoint
if a.intersection(b).is_empty() {
    // No flags in common
}

Performance:

// intersection() is O(1) - single bitwise AND
// No loops, no allocations, one CPU instruction
// Much faster than HashSet intersection

Key insight: intersection implements set intersection using bitwise AND, enabling extremely fast (single instruction) computation of which flags are shared between two sets. This is fundamental for permission checking (does user have all required?), capability negotiation (what can both parties do?), and filtering (which items are in both sets?). The operation is symmetric (a.intersection(b) == b.intersection(a)) and idempotent (a.intersection(a) == a). Combined with union (OR) and difference (AND NOT), intersection forms a complete set algebra for bit flags, enabling expressive queries about flag relationships while maintaining O(1) performance.