Loading page…
Rust walkthroughs
Loading page…
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:
|, intersect with &, toggle with ^# 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));
}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());
}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());
}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);
}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);
}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());
}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);
}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());
}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);
}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);
}
}// 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);
}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);
}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);
}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());
}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);
}
}
}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());
}bitflags! macro to define flag types backed by integers1 << n or explicit hex/octal/binary values|, intersect with &, toggle with ^, complement with !contains(flag) checks if a flag is setinsert(flag), remove(flag), toggle(flag) modify flags in placeempty() returns no flags, all() returns all defined flagsfrom_bits() returns Option<Flags> — None if undefined bits presentfrom_bits_truncate() removes undefined bitsfrom_bits_retain() keeps all bits including undefined onesiter() yields each set flag individually#[derive(Debug, Clone, Copy, PartialEq, Eq)] for common traitsserde feature for JSON/binary serializationbits() to get raw integer value for storage/transmission