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 operandsKey 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 intersectionKey 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.
