Loading page…
Rust walkthroughs
Loading page…
The bitflags crate provides a macro for defining bitflag types — types that represent a set of boolean flags stored as bits in an integer. This is useful for configuration options, permissions, state machines, and interfacing with C APIs or hardware. The crate generates a struct with bitwise operators and utility methods for manipulating flag sets efficiently.
Key features:
bitflags! macro — defines a bitflag type|, &, ^, ! for combining and manipulating flagscontains, insert, remove, toggle# Cargo.toml
[dependencies]
bitflags = "2"use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
}
}
fn main() {
let flags = Flags::A | Flags::C;
println!("Flags: {:?}", flags);
println!("Contains A: {}", flags.contains(Flags::A));
println!("Contains B: {}", flags.contains(Flags::B));
}use bitflags::bitflags;
bitflags! {
/// Permissions for file access
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Permissions: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
const ALL = Self::READ.bits() | Self::WRITE.bits() | Self::EXECUTE.bits();
}
}
fn main() {
// Create empty flags
let empty = Permissions::empty();
println!("Empty: {:?}", empty);
// Create with all flags
let all = Permissions::all();
println!("All: {:?}", all);
// Combine flags with OR
let read_write = Permissions::READ | Permissions::WRITE;
println!("Read + Write: {:?}", read_write);
// Use predefined combination
println!("All (predefined): {:?}", Permissions::ALL);
}
// Different underlying types
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct U8Flags: u8 {
const FLAG_A = 0b00000001;
const FLAG_B = 0b00000010;
const FLAG_C = 0b00000100;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct U64Flags: u64 {
const BIT_0 = 1 << 0;
const BIT_10 = 1 << 10;
const BIT_63 = 1 << 63;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct HexFlags: u32 {
const OPTION_1 = 0x0001;
const OPTION_2 = 0x0002;
const OPTION_3 = 0x0004;
const OPTION_4 = 0x0008;
}
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
const D = 0b1000;
}
}
fn main() {
let mut flags = Flags::A | Flags::B;
println!("Initial: {:?}", flags);
// Contains - check if flag is set
println!("Contains A: {}", flags.contains(Flags::A));
println!("Contains C: {}", flags.contains(Flags::C));
// Insert - add a flag
flags.insert(Flags::C);
println!("After insert C: {:?}", flags);
// Remove - remove a flag
flags.remove(Flags::A);
println!("After remove A: {:?}", flags);
// Toggle - flip a flag
flags.toggle(Flags::B);
println!("After toggle B: {:?}", flags);
// Set - replace all flags
flags.set(Flags::D, true);
println!("After set D true: {:?}", flags);
flags.set(Flags::C, false);
println!("After set C false: {:?}", flags);
// Empty and All
println!("Empty: {:?}", Flags::empty());
println!("All: {:?}", Flags::all());
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
}
}
fn main() {
let a = Flags::A;
let b = Flags::B;
let ab = Flags::A | Flags::B;
// OR - combine flags
let combined = Flags::A | Flags::B | Flags::C;
println!("A | B | C = {:?}", combined);
// AND - intersect flags
let intersect = ab & Flags::A;
println!("(A | B) & A = {:?}", intersect);
// XOR - toggle specific flags
let xor = ab ^ Flags::A;
println!("(A | B) ^ A = {:?}", xor);
// NOT - invert all flags (within the defined bit range)
let not = !Flags::A;
println!("!A = {:?}", not);
// Difference - remove flags
let diff = (Flags::A | Flags::B | Flags::C) - (Flags::B | Flags::C);
println!("Difference: {:?}", diff);
// Assignment operators
let mut flags = Flags::A;
flags |= Flags::B;
println!("After |= B: {:?}", flags);
flags &= Flags::A | Flags::B;
println!("After &= (A | B): {:?}", flags);
flags ^= Flags::A;
println!("After ^= A: {:?}", flags);
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Permissions: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
const ADMIN = 0b1000;
}
}
fn main() {
let user_perms = Permissions::READ | Permissions::WRITE;
let admin_perms = Permissions::all();
let exec_perms = Permissions::EXECUTE;
// Contains - check if all specified flags are set
println!("User can read: {}", user_perms.contains(Permissions::READ));
println!("User can execute: {}", user_perms.contains(Permissions::EXECUTE));
println!("User can read & write: {}",
user_perms.contains(Permissions::READ | Permissions::WRITE));
// Intersects - check if any flag is shared
println!("User and exec intersect: {}",
user_perms.intersects(exec_perms));
let rw_perms = Permissions::READ | Permissions::WRITE;
println!("User and RW intersect: {}",
user_perms.intersects(rw_perms));
// Union - combine all flags
let combined = user_perms.union(exec_perms);
println!("Union: {:?}", combined);
// Intersection - shared flags only
let shared = user_perms.intersection(rw_perms);
println!("Intersection: {:?}", shared);
// Difference - flags in self but not other
let diff = admin_perms.difference(user_perms);
println!("Admin - User: {:?}", diff);
// Symmetric difference - flags in either but not both
let sym_diff = user_perms.symmetric_difference(exec_perms);
println!("Sym diff: {:?}", sym_diff);
// Complement - all flags not set
let complement = user_perms.complement();
println!("Complement of user: {:?}", complement);
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Status: u8 {
const RUNNING = 0b0001;
const PAUSED = 0b0010;
const ERROR = 0b0100;
const COMPLETE = 0b1000;
}
}
fn main() {
let status = Status::RUNNING | Status::ERROR;
// Check single flag
if status.contains(Status::RUNNING) {
println!("Process is running");
}
// Check multiple flags (all must be present)
if status.contains(Status::RUNNING | Status::ERROR) {
println!("Process is running with error");
}
// Check if any flag is set
if status.intersects(Status::PAUSED | Status::ERROR) {
println!("Process has issue (paused or error)");
}
// Check if exactly these flags
if status == Status::RUNNING | Status::ERROR {
println!("Exact match");
}
// Check if empty
if status.is_empty() {
println!("No flags set");
} else {
println!("Some flags set");
}
// Check if all flags are set
if status.is_all() {
println!("All flags set");
} else {
println!("Not all flags set");
}
// Complex conditions
let can_continue = !status.contains(Status::ERROR)
&& !status.contains(Status::PAUSED);
println!("Can continue: {}", can_continue);
}
// Helper functions
fn check_status(status: Status) {
println!("Status: {:?}", status);
// Check combinations
let active = status.contains(Status::RUNNING)
&& !status.contains(Status::PAUSED);
let failed = status.contains(Status::ERROR);
let done = status.contains(Status::COMPLETE);
match (active, failed, done) {
(true, false, false) => println!(" -> Running normally"),
(true, true, false) => println!(" -> Running with errors!"),
(false, false, true) => println!(" -> Completed successfully"),
(false, true, true) => println!(" -> Completed with errors"),
_ => println!(" -> Unknown state"),
}
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
}
}
fn main() {
let flags = Flags::A | Flags::C;
// Get bits value
let bits: u8 = flags.bits();
println!("Bits: {:08b}", bits);
// Create from bits (unsafe - may contain undefined bits)
let from_bits = Flags::from_bits(bits);
println!("From bits: {:?}", from_bits);
// Create from bits (truncate undefined bits)
let truncated = Flags::from_bits_truncate(0b1111);
println!("Truncated: {:?}", truncated);
// Create from bits (retain undefined bits)
let unchecked = Flags::from_bits_unchecked(0b1111);
println!("Unchecked: {:?}", unchecked);
// Create from bits (return None for undefined bits)
let strict = Flags::from_bits(0b1111);
println!("Strict: {:?}", strict); // None (0b1000 is undefined)
// Check if bits are valid
println!("Is valid 0b0111: {}", Flags::is_valid(0b0111));
println!("Is valid 0b1111: {}", Flags::is_valid(0b1111));
// All bits
println!("All bits: {:08b}", Flags::all().bits());
}
// Working with external values
fn from_external(value: u8) -> Option<Flags> {
Flags::from_bits(value)
}
fn to_external(flags: Flags) -> u8 {
flags.bits()
}
// Converting from integer
fn conversion_example() {
// From known value
let value: u8 = 0b0101;
let flags = Flags::from_bits_truncate(value);
println!("Flags from {}: {:?}", value, flags);
// Round-trip
let original = Flags::A | Flags::C;
let bits = original.bits();
let restored = Flags::from_bits(bits).unwrap();
assert_eq!(original, restored);
}# Cargo.toml
[dependencies]
bitflags = { version = "2", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"use bitflags::bitflags;
use serde::{Deserialize, Serialize};
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
struct Permissions: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
}
}
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 restored: Permissions = serde_json::from_str(&json).unwrap();
println!("Restored: {:?}", restored);
assert_eq!(perms, restored);
}
// With struct
#[derive(Debug, Serialize, Deserialize)]
struct FileEntry {
name: String,
permissions: Permissions,
}
fn struct_example() {
let entry = FileEntry {
name: "document.txt".to_string(),
permissions: Permissions::READ | Permissions::WRITE,
};
let json = serde_json::to_string_pretty(&entry).unwrap();
println!("{}", json);
let parsed: FileEntry = serde_json::from_str(&json).unwrap();
println!("Parsed: {:?}", parsed);
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FileMode: u32 {
// User permissions
const USER_READ = 0o400;
const USER_WRITE = 0o200;
const USER_EXEC = 0o100;
const USER_ALL = Self::USER_READ.bits() | Self::USER_WRITE.bits() | Self::USER_EXEC.bits();
// Group permissions
const GROUP_READ = 0o040;
const GROUP_WRITE = 0o020;
const GROUP_EXEC = 0o010;
const GROUP_ALL = Self::GROUP_READ.bits() | Self::GROUP_WRITE.bits() | Self::GROUP_EXEC.bits();
// Other permissions
const OTHER_READ = 0o004;
const OTHER_WRITE = 0o002;
const OTHER_EXEC = 0o001;
const OTHER_ALL = Self::OTHER_READ.bits() | Self::OTHER_WRITE.bits() | Self::OTHER_EXEC.bits();
// Common combinations
const ALL_READ = Self::USER_READ.bits() | Self::GROUP_READ.bits() | Self::OTHER_READ.bits();
const ALL_EXEC = Self::USER_EXEC.bits() | Self::GROUP_EXEC.bits() | Self::OTHER_EXEC.bits();
}
}
fn main() {
// Standard file: rw-r--r--
let file = FileMode::USER_READ | FileMode::USER_WRITE
| FileMode::GROUP_READ | FileMode::OTHER_READ;
println!("File mode: {:o}", file.bits());
// Executable: rwxr-xr-x
let exec = FileMode::USER_ALL | FileMode::GROUP_READ | FileMode::GROUP_EXEC
| FileMode::OTHER_READ | FileMode::OTHER_EXEC;
println!("Exec mode: {:o}", exec.bits());
// Check permissions
println!("User can read: {}", file.contains(FileMode::USER_READ));
println!("User can write: {}", file.contains(FileMode::USER_WRITE));
println!("User can execute: {}", file.contains(FileMode::USER_EXEC));
// Check if anyone can read
println!("Anyone can read: {}", file.contains(FileMode::ALL_READ));
// Make executable for user
let mut script = file;
script.insert(FileMode::USER_EXEC);
println!("Script mode: {:o}", script.bits());
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WindowStyle: u32 {
const VISIBLE = 0x0001;
const BORDER = 0x0002;
const TITLEBAR = 0x0004;
const RESIZABLE = 0x0008;
const MINIMIZABLE = 0x0010;
const MAXIMIZABLE = 0x0020;
const CLOSABLE = 0x0040;
const MODAL = 0x0080;
// Common presets
const STANDARD = Self::VISIBLE.bits()
| Self::BORDER.bits()
| Self::TITLEBAR.bits()
| Self::RESIZABLE.bits()
| Self::MINIMIZABLE.bits()
| Self::MAXIMIZABLE.bits()
| Self::CLOSABLE.bits();
const DIALOG = Self::VISIBLE.bits()
| Self::BORDER.bits()
| Self::TITLEBAR.bits()
| Self::CLOSABLE.bits()
| Self::MODAL.bits();
}
}
struct Window {
title: String,
style: WindowStyle,
}
impl Window {
fn new(title: &str, style: WindowStyle) -> Self {
Self {
title: title.to_string(),
style,
}
}
fn is_visible(&self) -> bool {
self.style.contains(WindowStyle::VISIBLE)
}
fn is_resizable(&self) -> bool {
self.style.contains(WindowStyle::RESIZABLE)
}
fn is_modal(&self) -> bool {
self.style.contains(WindowStyle::MODAL)
}
fn add_style(&mut self, style: WindowStyle) {
self.style.insert(style);
}
fn remove_style(&mut self, style: WindowStyle) {
self.style.remove(style);
}
}
fn main() {
// Standard window
let window = Window::new("Main Window", WindowStyle::STANDARD);
println!("Window visible: {}", window.is_visible());
println!("Window resizable: {}", window.is_resizable());
println!("Window modal: {}", window.is_modal());
// Dialog
let dialog = Window::new("Settings", WindowStyle::DIALOG);
println!("\nDialog modal: {}", dialog.is_modal());
println!("Dialog resizable: {}", dialog.is_resizable());
// Custom window
let mut custom = Window::new("Custom", WindowStyle::VISIBLE | WindowStyle::BORDER);
println!("\nCustom resizable: {}", custom.is_resizable());
custom.add_style(WindowStyle::RESIZABLE);
println!("After adding: {}", custom.is_resizable());
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConfigFlags: u32 {
const DEBUG = 1 << 0;
const VERBOSE = 1 << 1;
const LOG_FILE = 1 << 2;
const LOG_CONSOLE = 1 << 3;
const COLOR_OUTPUT = 1 << 4;
const TIMESTAMP = 1 << 5;
const THREAD_SAFE = 1 << 6;
const AUTO_SAVE = 1 << 7;
// Common presets
const DEBUG_MODE = Self::DEBUG.bits()
| Self::VERBOSE.bits()
| Self::LOG_CONSOLE.bits()
| Self::COLOR_OUTPUT.bits();
const PRODUCTION = Self::LOG_FILE.bits()
| Self::THREAD_SAFE.bits()
| Self::AUTO_SAVE.bits();
}
}
struct Logger {
flags: ConfigFlags,
}
impl Logger {
fn new(flags: ConfigFlags) -> Self {
Self { flags }
}
fn is_debug(&self) -> bool {
self.flags.contains(ConfigFlags::DEBUG)
}
fn is_verbose(&self) -> bool {
self.flags.contains(ConfigFlags::VERBOSE)
}
fn should_log_to_file(&self) -> bool {
self.flags.contains(ConfigFlags::LOG_FILE)
}
fn should_log_to_console(&self) -> bool {
self.flags.contains(ConfigFlags::LOG_CONSOLE)
}
fn log(&self, message: &str) {
let timestamp = if self.flags.contains(ConfigFlags::TIMESTAMP) {
format!("[{}] ", chrono::Local::now().format("%H:%M:%S"))
} else {
String::new()
};
let prefix = if self.is_debug() { "[DEBUG] " } else { "" };
if self.should_log_to_console() {
println!("{}{}{}", timestamp, prefix, message);
}
}
}
fn main() {
// Debug logger
let debug_logger = Logger::new(ConfigFlags::DEBUG_MODE);
println!("Debug mode: {}", debug_logger.is_debug());
println!("Verbose: {}", debug_logger.is_verbose());
// Production logger
let prod_logger = Logger::new(ConfigFlags::PRODUCTION);
println!("\nProduction:");
println!("Log to file: {}", prod_logger.should_log_to_file());
println!("Log to console: {}", prod_logger.should_log_to_console());
// Custom configuration
let mut custom_flags = ConfigFlags::LOG_CONSOLE | ConfigFlags::COLOR_OUTPUT;
custom_flags.insert(ConfigFlags::TIMESTAMP);
let custom_logger = Logger::new(custom_flags);
println!("\nCustom logger:");
custom_logger.log("Hello, world!");
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub 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;
}
}
struct TcpHeader {
flags: TcpFlags,
seq_num: u32,
ack_num: u32,
}
impl TcpHeader {
fn new(flags: TcpFlags) -> Self {
Self {
flags,
seq_num: 0,
ack_num: 0,
}
}
fn is_syn(&self) -> bool {
self.flags.contains(TcpFlags::SYN)
}
fn is_ack(&self) -> bool {
self.flags.contains(TcpFlags::ACK)
}
fn is_syn_ack(&self) -> bool {
self.flags.contains(TcpFlags::SYN | TcpFlags::ACK)
}
fn is_fin(&self) -> bool {
self.flags.contains(TcpFlags::FIN)
}
fn is_rst(&self) -> bool {
self.flags.contains(TcpFlags::RST)
}
fn flags_value(&self) -> u8 {
self.flags.bits()
}
}
fn main() {
// SYN packet (connection initiation)
let syn = TcpHeader::new(TcpFlags::SYN);
println!("SYN packet: flags = {:08b}", syn.flags_value());
// SYN-ACK packet (connection response)
let syn_ack = TcpHeader::new(TcpFlags::SYN | TcpFlags::ACK);
println!("SYN-ACK packet: flags = {:08b}", syn_ack.flags_value());
println!("Is SYN-ACK: {}", syn_ack.is_syn_ack());
// FIN packet (connection close)
let fin = TcpHeader::new(TcpFlags::FIN | TcpFlags::ACK);
println!("FIN-ACK packet: flags = {:08b}", fin.flags_value());
// RST packet (connection reset)
let rst = TcpHeader::new(TcpFlags::RST);
println!("RST packet: flags = {:08b}", rst.flags_value());
// Data packet with PSH-ACK
let data = TcpHeader::new(TcpFlags::PSH | TcpFlags::ACK);
println!("Data packet: flags = {:08b}", data.flags_value());
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
const D = 0b1000;
}
}
fn main() {
let flags = Flags::A | Flags::C | Flags::D;
// Iterate over all defined flags
println!("All defined flags:");
for flag in Flags::all().iter() {
println!(" {:?} = {:04b}", flag, flag.bits());
}
// Iterate over set flags
println!("\nSet flags in {:?}:", flags);
for flag in flags.iter() {
println!(" {:?}", flag);
}
// Count set flags
let count = flags.iter().count();
println!("\nNumber of set flags: {}", count);
// Collect into vector
let set_flags: Vec<Flags> = flags.iter().collect();
println!("Set flags: {:?}", set_flags);
// Check each flag
println!("\nChecking each flag:");
for flag in Flags::all().iter() {
if flags.contains(flag) {
println!(" {:?} is set", flag);
} else {
println!(" {:?} is NOT set", flag);
}
}
}
// Manual iteration
fn manual_iterate() {
let flags = Flags::A | Flags::B;
// Check each known flag
let all_flags = [Flags::A, Flags::B, Flags::C, Flags::D];
println!("Manual iteration:");
for flag in all_flags {
if flags.contains(flag) {
println!(" {:?} is set", flag);
}
}
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Permissions: u8 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
}
}
fn main() {
let perms = Permissions::READ | Permissions::WRITE;
// Debug format
println!("Debug: {:?}", perms);
// Binary format
println!("Binary: {:08b}", perms.bits());
// Octal format
println!("Octal: {:03o}", perms.bits());
// Hex format
println!("Hex: {:02x}", perms.bits());
// Custom display
display_permissions(perms);
}
fn display_permissions(perms: Permissions) {
let mut parts = Vec::new();
if perms.contains(Permissions::READ) {
parts.push("READ");
}
if perms.contains(Permissions::WRITE) {
parts.push("WRITE");
}
if perms.contains(Permissions::EXECUTE) {
parts.push("EXECUTE");
}
if parts.is_empty() {
println!("Permissions: none");
} else {
println!("Permissions: {}", parts.join(" | "));
}
}
// Unix-style permission string
fn unix_permissions(perms: Permissions) -> String {
let r = if perms.contains(Permissions::READ) { 'r' } else { '-' };
let w = if perms.contains(Permissions::WRITE) { 'w' } else { '-' };
let x = if perms.contains(Permissions::EXECUTE) { 'x' } else { '-' };
format!("{}{}{}", r, w, x)
}use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Modifiers: u8 {
const SHIFT = 0b0001;
const CTRL = 0b0010;
const ALT = 0b0100;
const META = 0b1000;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Key {
Char(char),
Enter,
Escape,
Tab,
Backspace,
Delete,
ArrowUp,
ArrowDown,
ArrowLeft,
ArrowRight,
}
#[derive(Debug, Clone, Copy)]
struct KeyEvent {
key: Key,
modifiers: Modifiers,
}
impl KeyEvent {
fn new(key: Key) -> Self {
Self {
key,
modifiers: Modifiers::empty(),
}
}
fn with_modifiers(key: Key, modifiers: Modifiers) -> Self {
Self { key, modifiers }
}
fn has_shift(&self) -> bool {
self.modifiers.contains(Modifiers::SHIFT)
}
fn has_ctrl(&self) -> bool {
self.modifiers.contains(Modifiers::CTRL)
}
fn has_alt(&self) -> bool {
self.modifiers.contains(Modifiers::ALT)
}
}
fn main() {
// Simple key
let key_a = KeyEvent::new(Key::Char('a'));
println!("Key: {:?}, modifiers: {:?}", key_a.key, key_a.modifiers);
// Ctrl+C
let ctrl_c = KeyEvent::with_modifiers(Key::Char('c'), Modifiers::CTRL);
println!("Ctrl+C: has_ctrl={}", ctrl_c.has_ctrl());
// Ctrl+Shift+S
let ctrl_shift_s = KeyEvent::with_modifiers(
Key::Char('s'),
Modifiers::CTRL | Modifiers::SHIFT,
);
println!("Ctrl+Shift+S: {:?}", ctrl_shift_s.modifiers);
// Alt+ArrowUp
let alt_up = KeyEvent::with_modifiers(Key::ArrowUp, Modifiers::ALT);
println!("Alt+Up: has_alt={}", alt_up.has_alt());
}bitflags! macro to define bit flag types| operator: Flags::A | Flags::Bcontains(): flags.contains(Flags::A)insert(), remove(), toggle(), set()empty() for no flags, all() for all flags.bits()from_bits(), from_bits_truncate(), from_bits_unchecked()flags.iter()#[derive(...)] inside the macro for traits like Debug, Clone, Hash