What is the purpose of bitflags::Flags::intersection for computing overlapping bits between flag sets?
intersection computes the bitwise AND of two flag sets, returning a new flag set containing only the bits that are set in both operands. This operation answers the question "which flags are enabled in both sets?"âuseful for checking capability requirements, filtering options, and validating configuration. The result preserves the flag types, so you get a properly typed flags value rather than a raw integer.
Basic Intersection Operations
use bitflags::Flags;
#[derive(Debug, Clone, Copy)]
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Permissions: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
const ADMIN = 0b1000;
}
}
fn main() {
let user_permissions = Permissions::READ | Permissions::WRITE;
let required_permissions = Permissions::READ | Permissions::EXECUTE;
// intersection: bits set in BOTH flag sets
let common = user_permissions.intersection(required_permissions);
println!("Common permissions: {:?}", common);
// Common permissions: Permissions(READ)
// Only READ is in both sets
// WRITE is only in user_permissions
// EXECUTE is only in required_permissions
// Alternative syntax using & operator
let also_common = user_permissions & required_permissions;
assert_eq!(common, also_common);
}intersection returns flags present in both setsâit's the set-theoretic intersection.
Intersection for Capability Checking
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Capabilities: u32 {
const CREATE = 0b0001;
const READ = 0b0010;
const UPDATE = 0b0100;
const DELETE = 0b1000;
}
}
fn main() {
let required = Capabilities::CREATE | Capabilities::READ | Capabilities::DELETE;
let granted = Capabilities::CREATE | Capabilities::READ | Capabilities::UPDATE;
// Check if all required capabilities are granted
let missing = required.intersection(granted);
// Wait, this gives us what's common, not what's missing
// Better approach:
let has_all_required = (required & granted) == required;
println!("Has all required: {}", has_all_required);
// false - DELETE is required but not granted
// What's granted that's not required?
let extra = granted.difference(required); // Or: granted - required
println!("Extra capabilities: {:?}", extra);
// Extra capabilities: Capabilities(UPDATE)
// What's required that's missing?
let needed = required.difference(granted);
println!("Missing capabilities: {:?}", needed);
// Missing capabilities: Capabilities(DELETE)
// Intersection tells us what's common
let common = required.intersection(granted);
println!("Common capabilities: {:?}", common);
// Common capabilities: Capabilities(CREATE | READ)
}Intersection finds common capabilities; combine with difference to find missing or extra.
Intersection vs Other Set Operations
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
const D = 0b1000;
}
}
fn main() {
let set1 = Flags::A | Flags::B | Flags::C;
let set2 = Flags::B | Flags::C | Flags::D;
// intersection (&): bits in BOTH sets
let intersection = set1.intersection(set2);
println!("Intersection: {:?}", intersection);
// Intersection: Flags(B | C)
// union (|): bits in EITHER set
let union = set1.union(set2);
println!("Union: {:?}", union);
// Union: Flags(A | B | C | D)
// difference (-): bits in first but NOT second
let difference = set1.difference(set2);
println!("Difference: {:?}", difference);
// Difference: Flags(A)
// symmetric_difference (^): bits in exactly ONE set
let sym_diff = set1.symmetric_difference(set2);
println!("Symmetric difference: {:?}", sym_diff);
// Symmetric difference: Flags(A | D)
// complement (!): bits NOT in this set
let complement = set1.complement();
println!("Complement: {:?}", complement);
// Complement: Flags(D) (assuming only A, B, C, D defined)
}Intersection is one of several set operationsâuse it to find overlapping bits.
Checking for Overlap
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Options: u32 {
const VERBOSE = 0b0001;
const DEBUG = 0b0010;
const TRACE = 0b0100;
const QUIET = 0b1000;
}
}
fn main() {
let enabled = Options::VERBOSE | Options::DEBUG;
let conflicting = Options::QUIET | Options::TRACE;
// Check if any bits overlap
let overlap = enabled.intersection(conflicting);
if overlap.is_empty() {
println!("No conflicts");
} else {
println!("Conflicting options: {:?}", overlap);
}
// No conflicts
// Check if a specific flag is set
if enabled.contains(Options::VERBOSE) {
println!("Verbose mode enabled");
}
// Check if ANY of a set of flags are set
let any_debug = enabled.intersection(Options::DEBUG | Options::TRACE);
if !any_debug.is_empty() {
println!("Some debug mode enabled: {:?}", any_debug);
}
// Some debug mode enabled: Options(DEBUG)
}Intersection with is_empty() checks whether two flag sets share any bits.
Intersection for Filtering
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Features: u32 {
const FEATURE_A = 0b0001;
const FEATURE_B = 0b0010;
const FEATURE_C = 0b0100;
const FEATURE_D = 0b1000;
}
}
fn main() {
let requested = Features::FEATURE_A | Features::FEATURE_B | Features::FEATURE_C;
let supported = Features::FEATURE_A | Features::FEATURE_C | Features::FEATURE_D;
// Find which requested features are actually supported
let available = requested.intersection(supported);
println!("Available features: {:?}", available);
// Available features: Features(FEATURE_A | FEATURE_C)
// Which requested features are NOT supported?
let unsupported = requested.difference(supported);
println!("Unsupported features: {:?}", unsupported);
// Unsupported features: Features(FEATURE_B)
// Which supported features were NOT requested?
let unrequested = supported.difference(requested);
println!("Unrequested supported features: {:?}", unrequested);
// Unrequested supported features: Features(FEATURE_D)
// Use intersection to filter a list
let feature_requests = [
Features::FEATURE_A | Features::FEATURE_B,
Features::FEATURE_C,
Features::FEATURE_B | Features::FEATURE_D,
];
let supported_features = Features::FEATURE_A | Features::FEATURE_C;
for (i, request) in feature_requests.iter().enumerate() {
let can_provide = request.intersection(supported_features);
if !can_provide.is_empty() {
println!("Request {} can provide: {:?}", i, can_provide);
} else {
println!("Request {} cannot be fulfilled", i);
}
}
// Request 0 can provide: Features(FEATURE_A)
// Request 1 can provide: Features(FEATURE_C)
// Request 2 cannot be fulfilled
}Intersection helps filter and validate feature requests against available capabilities.
Type-Safe Results
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq)]
struct Permissions: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
}
}
fn main() {
let user = Permissions::READ | Permissions::WRITE;
let required = Permissions::READ | Permissions::EXECUTE;
// intersection returns Permissions, not u32
let common: Permissions = user.intersection(required);
// This means you can use all flags methods on the result
if common.contains(Permissions::READ) {
println!("Read permission is common");
}
// Can iterate over flags in the intersection
let flags: Vec<Permissions> = common.iter().collect();
println!("Flags in intersection: {:?}", flags);
// Flags in intersection: [Permissions(READ)]
// Can check emptiness
let empty = Permissions::empty();
let result = user.intersection(empty);
assert!(result.is_empty());
// Can convert to bits
let bits = common.bits();
println!("Bits: {:b}", bits);
// Bits: 1
}The result type matches the input typeâpreserving type safety for further operations.
Intersection in Conditional Logic
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct LogLevel: u8 {
const ERROR = 0b0001;
const WARN = 0b0010;
const INFO = 0b0100;
const DEBUG = 0b1000;
}
}
fn main() {
let configured = LogLevel::ERROR | LogLevel::WARN | LogLevel::INFO;
let logged_event = LogLevel::DEBUG | LogLevel::INFO;
// Should we log this event?
let should_log = configured.intersection(logged_event);
if !should_log.is_empty() {
println!("Logging event: {:?}", should_log);
}
// Logging event: LogLevel(INFO)
// Alternative: contains for single flag check
if configured.contains(LogLevel::INFO) {
println!("INFO level is configured");
}
// Intersection is useful when you have multiple flags to check
let event_levels = LogLevel::INFO | LogLevel::DEBUG;
if !configured.intersection(event_levels).is_empty() {
println!("At least one event level is configured");
}
}Intersection handles checking multiple flags at once versus single flag checks.
Performance Characteristics
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u64 {
const A = 1 << 0;
const B = 1 << 1;
// ... up to 64 flags for u64
}
}
fn main() {
let set1 = Flags::A | Flags::B;
let set2 = Flags::A;
// Intersection is a single bitwise AND operation
// O(1) - just one CPU instruction
let common = set1.intersection(set2);
// For larger flag sets (u128, etc.), still O(1)
// Just operates on the underlying integer
// Comparison with iteration-based approaches:
fn manual_intersection(set1: Flags, set2: Flags) -> Flags {
let mut result = Flags::empty();
for flag in set1.iter() {
if set2.contains(flag) {
result |= flag;
}
}
result
}
// The manual approach is correct but slower
// Intersection uses bitwise AND: one instruction
let a = Flags::A | Flags::B;
let b = Flags::A;
assert_eq!(a.intersection(b), manual_intersection(a, b));
}Intersection compiles to a single bitwise ANDâextremely efficient.
Intersection with Empty Sets
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Status: u8 {
const ACTIVE = 0b0001;
const PENDING = 0b0010;
const CLOSED = 0b0100;
}
}
fn main() {
let status = Status::ACTIVE;
// Intersection with empty set
let empty = Status::empty();
let result = status.intersection(empty);
assert!(result.is_empty());
println!("Status & empty = {:?}", result);
// Status & empty = Status(0x0)
// Intersection with self
let same = status.intersection(status);
assert_eq!(same, status);
println!("Status & Status = {:?}", same);
// Status & Status = Status(ACTIVE)
// Intersection with all flags
let all = Status::all();
let result = status.intersection(all);
assert_eq!(result, status);
println!("Status & all = {:?}", result);
// Status & all = Status(ACTIVE)
// Intersection with complement
let not_active = status.complement();
let result = status.intersection(not_active);
assert!(result.is_empty());
println!("Status & !Status = {:?}", result);
// Status & !Status = Status(0x0)
}Intersection with empty or all flags follows predictable set-theoretic rules.
Practical Pattern: Permission Checking
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Permission: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const DELETE = 0b0100;
const SHARE = 0b1000;
const ADMIN = 0b1111; // All permissions
}
}
fn main() {
let user_permissions = Permission::READ | Permission::WRITE;
let resource_requirements = Permission::READ | Permission::DELETE;
// Can user access this resource?
// They need ALL required permissions
let missing = resource_requirements.difference(user_permissions);
if missing.is_empty() {
println!("Access granted");
} else {
println!("Access denied. Missing: {:?}", missing);
// Access denied. Missing: Permission(DELETE)
}
// What can the user do with this resource?
let allowed = user_permissions.intersection(resource_requirements);
println!("Allowed operations: {:?}", allowed);
// Allowed operations: Permission(READ)
// For read-only access, only READ is needed
let read_only = Permission::READ;
let can_read = user_permissions.intersection(read_only);
if !can_read.is_empty() {
println!("Can read the resource");
}
// Check multiple requirements
fn check_permission(user: Permission, required: Permission) -> bool {
// User must have ALL required permissions
(user & required) == required
}
// Check partial permission (at least one)
fn has_any_permission(user: Permission, any_of: Permission) -> bool {
!user.intersection(any_of).is_empty()
}
assert!(!check_permission(user_permissions, resource_requirements));
assert!(has_any_permission(user_permissions, Permission::DELETE | Permission::READ));
}Intersection helps implement permission checking patterns with partial and full matching.
Synthesis
Quick reference:
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
}
}
fn main() {
let set1 = Flags::A | Flags::B;
let set2 = Flags::B | Flags::C;
// intersection(&self, other) -> Flags
// Returns flags present in BOTH sets
let common = set1.intersection(set2);
// Equivalent to: set1 & set2
// Result is a Flags type, not a raw integer
assert_eq!(common, Flags::B);
// Use cases:
// 1. Find common capabilities/permissions
// 2. Check if two sets have any overlap
// 3. Filter features/options
// 4. Validate partial requirements
// Common patterns:
// Check overlap: !a.intersection(b).is_empty()
// Check subset: (a & b) == a (a is subset of b)
// Check superset: (a & b) == b (b is subset of a)
// Performance: Single bitwise AND operation
// O(1) regardless of number of flags
}Key insight: intersection is the set-theoretic intersection operation for bitflagsâit returns a new flag set containing only bits set in both operands. This is implemented as a single bitwise AND, making it extremely efficient. Use intersection when you need to find common flags between two sets, check for overlap, or validate capabilities. The result preserves the flag type, allowing further operations. Combine with is_empty() to check for any overlap, or compare to specific flags to implement subset/superset logic. The & operator is syntactic sugar for intersection, so a & b and a.intersection(b) are equivalent.
