Loading pageā¦
Rust walkthroughs
Loading pageā¦
bitflags::Flags::bits and from_bits_retain for flag value handling?bitflags::Flags::bits and from_bits_retain handle unknown bitsābits set in a value that don't correspond to any defined flagādifferently. The bits() method returns the raw underlying bits value, potentially including unknown bits depending on how the flags were constructed. The from_bits_retain() constructor creates a flags value from bits while preserving any unknown bits, allowing round-trip conversion without data loss. This contrasts with from_bits() which returns None if unknown bits are present, making from_bits_retain() essential for bitwise operations that may set bits outside the defined flags.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Permissions: u32 {
const READ = 0b001;
const WRITE = 0b010;
const EXECUTE = 0b100;
}
}
fn main() {
let perms = Permissions::READ | Permissions::WRITE;
println!("Permissions: {:?}", perms);
println!("Bits: {:03b}", perms.bits());
}Bitflags are defined with named constants representing individual bit positions.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Status: u8 {
const ACTIVE = 0b00000001;
const PENDING = 0b00000010;
const VERIFIED = 0b00000100;
}
}
fn main() {
let status = Status::ACTIVE | Status::PENDING;
// bits() returns the raw underlying value
let raw_bits: u8 = status.bits();
println!("Status: {:?}", status);
println!("Raw bits: {:08b} ({})", raw_bits, raw_bits);
// Check individual flags
println!("Has ACTIVE: {}", status.contains(Status::ACTIVE));
println!("Has PENDING: {}", status.contains(Status::PENDING));
println!("Has VERIFIED: {}", status.contains(Status::VERIFIED));
}bits() returns the raw numeric value stored in the flags type.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Color: u8 {
const RED = 0b001;
const GREEN = 0b010;
const BLUE = 0b100;
}
}
fn main() {
// from_bits: returns None if unknown bits present
let known = Color::from_bits(0b101); // RED | BLUE
println!("Known bits: {:?}", known);
let unknown = Color::from_bits(0b1001); // Has unknown bit
println!("Unknown bits: {:?}", unknown); // None
// from_bits_truncate: removes unknown bits
let truncated = Color::from_bits_truncate(0b1001);
println!("Truncated: {:?} (bits: {:03b})", truncated, truncated.bits());
// from_bits_retain: preserves unknown bits
let retained = Color::from_bits_retain(0b1001);
println!("Retained: {:?} (bits: {:03b})", retained, retained.bits());
}from_bits returns None for unknown bits; from_bits_truncate removes them; from_bits_retain keeps them.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Flags: u8 {
const A = 0b0001;
const B = 0b0010;
const C = 0b0100;
}
}
fn main() {
// Value with unknown bits (0b1000)
let raw_value: u8 = 0b1111;
// from_bits rejects unknown bits
match Flags::from_bits(raw_value) {
Some(flags) => println!("from_bits: {:?}", flags),
None => println!("from_bits: None (unknown bits present)"),
}
// from_bits_truncate removes unknown bits
let truncated = Flags::from_bits_truncate(raw_value);
println!("from_bits_truncate: {:?} = {:04b}", truncated, truncated.bits());
// from_bits_retain preserves unknown bits
let retained = Flags::from_bits_retain(raw_value);
println!("from_bits_retain: {:?} = {:04b}", retained, retained.bits());
// bits() returns all bits including unknown
println!("Retained bits: {:04b}", retained.bits());
}The three methods handle unknown bits differently: reject, truncate, or retain.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Mode: u16 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
}
}
fn round_trip_truncate(bits: u16) -> u16 {
let flags = Mode::from_bits_truncate(bits);
flags.bits()
}
fn round_trip_retain(bits: u16) -> u16 {
let flags = Mode::from_bits_retain(bits);
flags.bits()
}
fn main() {
let original: u16 = 0b0111_1111; // Mix of known and unknown bits
println!("Original: {:016b}", original);
// Truncate loses unknown bits
let truncated = round_trip_truncate(original);
println!("After truncate: {:016b} (lost bits)", truncated);
println!("Bits lost: {:016b}", original & !truncated);
// Retain preserves all bits
let retained = round_trip_retain(original);
println!("After retain: {:016b} (preserved)", retained);
println!("Identical: {}", original == retained);
}from_bits_retain enables lossless round-trip conversion between flags and raw bits.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct FileMode: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
}
}
fn parse_file_mode_from_system(raw_mode: u32) -> FileMode {
// External system may have bits we don't recognize
// Use from_bits_retain to preserve them
FileMode::from_bits_retain(raw_mode)
}
fn send_file_mode_to_system(mode: FileMode) -> u32 {
// Send back all bits, including unknown ones
mode.bits()
}
fn main() {
// System returns mode with future bits
let system_mode: u32 = 0b1111_0000_0111; // Unknown upper bits
let parsed = parse_file_mode_from_system(system_mode);
println!("Parsed flags: {:?}", parsed);
println!("Parsed bits: {:032b}", parsed.bits());
// Can still work with known bits
println!("Has READ: {}", parsed.contains(FileMode::READ));
println!("Has WRITE: {}", parsed.contains(FileMode::WRITE));
// Send back preserves everything
let returned = send_file_mode_to_system(parsed);
println!("Returned: {:032b}", returned);
println!("Preserved: {}", system_mode == returned);
}from_bits_retain is essential when interoperating with systems that may define additional bits.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Options: u8 {
const A = 1 << 0;
const B = 1 << 1;
const C = 1 << 2;
}
}
fn main() {
// Start with known flags
let opts = Options::A | Options::B;
println!("Initial: {:?} = {:08b}", opts, opts.bits());
// Set a bit that doesn't correspond to any flag
let extended = opts.bits() | (1 << 7);
let opts_with_unknown = Options::from_bits_retain(extended);
println!("With unknown: {:?} = {:08b}", opts_with_unknown, opts_with_unknown.bits());
// Still contains known flags
println!("Has A: {}", opts_with_unknown.contains(Options::A));
println!("Has B: {}", opts_with_unknown.contains(Options::B));
println!("Has C: {}", opts_with_unknown.contains(Options::C));
// Unknown bit preserved
let unknown_bit = opts_with_unknown.bits() & !(Options::all().bits());
println!("Unknown bits: {:08b}", unknown_bit);
}Bitwise operations may set unknown bits; from_bits_retain preserves them.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Features: u8 {
const FEATURE_A = 0b0001;
const FEATURE_B = 0b0010;
}
}
fn main() {
// Create flags with unknown bits
let with_unknown = Features::from_bits_retain(0b1001);
// Union: combines all bits
let combined = with_unknown.union(Features::FEATURE_B);
println!("Union: {:?} = {:08b}", combined, combined.bits());
// Intersection: keeps common bits
let common = with_unknown.intersection(Features::all());
println!("Intersection: {:?} = {:08b}", common, common.bits());
// Difference: removes specified bits
let diff = with_unknown.difference(Features::FEATURE_A);
println!("Difference: {:?} = {:08b}", diff, diff.bits());
// Symmetric difference: XOR
let sym_diff = with_unknown.symmetric_difference(Features::all());
println!("Sym diff: {:?} = {:08b}", sym_diff, sym_diff.bits());
}Set operations work with unknown bits when using from_bits_retain.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Config: u8 {
const DEBUG = 0b0001;
const VERBOSE = 0b0010;
const QUIET = 0b0100;
}
}
fn has_unknown_bits(flags: Config) -> bool {
// Check if any bits are not in the defined flags
flags.bits() != flags.intersection(Config::all()).bits()
}
fn get_unknown_bits(flags: Config) -> u8 {
// Get bits that don't correspond to any defined flag
flags.bits() & !Config::all().bits()
}
fn main() {
let known = Config::DEBUG | Config::VERBOSE;
println!("Known: {:?}, unknown bits: {}", known, has_unknown_bits(known));
let with_unknown = Config::from_bits_retain(0b1111);
println!("With unknown: {:?}, unknown bits: {}", with_unknown, has_unknown_bits(with_unknown));
let unknown_value = get_unknown_bits(with_unknown);
println!("Unknown bits value: {:08b}", unknown_value);
// is_all() only true if ALL bits are known
println!("Is all: {}", with_unknown.is_all());
}You can detect and extract unknown bits for validation or logging.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Permissions: u16 {
const READ = 0b00000001;
const WRITE = 0b00000010;
const EXECUTE = 0b00000100;
}
}
#[derive(Debug)]
enum ParseResult {
Known(Permissions),
Unknown { known: Permissions, unknown_bits: u16 },
}
fn parse_permissions(bits: u16) -> ParseResult {
// Check for unknown bits
let unknown = bits & !Permissions::all().bits();
if unknown == 0 {
ParseResult::Known(Permissions::from_bits_retain(bits))
} else {
ParseResult::Unknown {
known: Permissions::from_bits_truncate(bits),
unknown_bits: unknown,
}
}
}
fn main() {
let known_bits: u16 = 0b0111;
let unknown_bits: u16 = 0b1111_1111;
match parse_permissions(known_bits) {
ParseResult::Known(perm) => println!("Known: {:?}", perm),
ParseResult::Unknown { known, unknown_bits } => {
println!("Has unknown bits: {:016b}", unknown_bits);
println!("Known part: {:?}", known);
}
}
match parse_permissions(unknown_bits) {
ParseResult::Known(perm) => println!("Known: {:?}", perm),
ParseResult::Unknown { known, unknown_bits } => {
println!("Has unknown bits: {:016b}", unknown_bits);
println!("Known part: {:?}", known);
}
}
}Different strategies for handling unknown bits depending on use case.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct UnixPerms: u16 {
// Owner
const OWNER_READ = 0o400;
const OWNER_WRITE = 0o200;
const OWNER_EXEC = 0o100;
// Group
const GROUP_READ = 0o040;
const GROUP_WRITE = 0o020;
const GROUP_EXEC = 0o010;
// Other
const OTHER_READ = 0o004;
const OTHER_WRITE = 0o002;
const OTHER_EXEC = 0o001;
// Special bits
const SETUID = 0o4000;
const SETGID = 0o2000;
const STICKY = 0o1000;
}
}
fn main() {
// Unix file mode 0o755: rwxr-xr-x
let mode_755 = UnixPerms::from_bits_retain(0o755);
println!("Mode 755: {:o}", mode_755.bits());
println!("Owner can read: {}", mode_755.contains(UnixPerms::OWNER_READ));
println!("Owner can write: {}", mode_755.contains(UnixPerms::OWNER_WRITE));
println!("Owner can execute: {}", mode_755.contains(UnixPerms::OWNER_EXEC));
// File mode with future bits (hypothetical)
let future_mode = 0o10755; // Has an extra bit we don't recognize
let preserved = UnixPerms::from_bits_retain(future_mode);
println!("Future mode preserved: {:o}", preserved.bits());
// Truncate would lose information
let truncated = UnixPerms::from_bits_truncate(future_mode);
println!("Truncated mode: {:o}", truncated.bits());
println!("Lost: {:o}", future_mode & !truncated.bits());
}File permissions from external sources may contain bits not defined in your enum.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct ControlRegister: u32 {
const ENABLE = 1 << 0;
const RESET = 1 << 1;
const INTERRUPT_ENABLE = 1 << 2;
const POWER_DOWN = 1 << 3;
// Hardware version may have additional bits
}
}
fn read_hardware_register() -> u32 {
// Hardware might have vendor-specific bits
0b1111_0000_1111 // Upper bits are vendor-specific
}
fn write_hardware_register(value: u32) {
println!("Writing to hardware: {:032b}", value);
}
fn main() {
// Read from hardware
let raw_value = read_hardware_register();
// Preserve all bits, work with known ones
let register = ControlRegister::from_bits_retain(raw_value);
// Check known bits
if register.contains(ControlRegister::ENABLE) {
println!("Device is enabled");
}
// Modify known bits
let modified = register.union(ControlRegister::RESET);
// Write back, preserving unknown bits
write_hardware_register(modified.bits());
// If we truncated, we'd lose vendor bits
let truncated = ControlRegister::from_bits_truncate(raw_value);
println!("Truncated loses: {:032b}", raw_value & !truncated.bits());
}Hardware registers often contain vendor-specific bits that must be preserved.
use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Flags: u8 {
const A = 0b01;
const B = 0b10;
}
}
fn main() {
let known_bits: u8 = 0b11; // A | B
let unknown_bits: u8 = 0b111; // A | B | unknown
println!("Input: {:08b}", known_bits);
println!(" from_bits: {:?}", Flags::from_bits(known_bits));
println!(" from_bits_truncate: {:?}", Flags::from_bits_truncate(known_bits));
println!(" from_bits_retain: {:?}", Flags::from_bits_retain(known_bits));
println!("\nInput: {:08b}", unknown_bits);
println!(" from_bits: {:?}", Flags::from_bits(unknown_bits));
println!(" from_bits_truncate: {:?}", Flags::from_bits_truncate(unknown_bits));
println!(" from_bits_retain: {:?}", Flags::from_bits_retain(unknown_bits));
println!("\nUnknown bits with retain:");
let retained = Flags::from_bits_retain(unknown_bits);
println!(" bits(): {:08b}", retained.bits());
println!(" all().bits(): {:08b}", Flags::all().bits());
println!(" unknown bits: {:08b}", retained.bits() & !Flags::all().bits());
}use bitflags::Flags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
bitflags::bitflags! {
#[derive(Default)]
struct Config: u8 {
const FEATURE_A = 1 << 0;
const FEATURE_B = 1 << 1;
const FEATURE_C = 1 << 2;
}
}
// When serializing/deserializing from external formats
fn deserialize_config(raw: u8, strict: bool) -> Result<Config, String> {
if strict {
// Strict mode: reject unknown bits
Config::from_bits(raw)
.ok_or_else(|| format!("Unknown bits in config: {:08b}", raw))
} else {
// Lenient mode: preserve unknown bits
Ok(Config::from_bits_retain(raw))
}
}
fn serialize_config(config: Config) -> u8 {
// Always preserves all bits
config.bits()
}
fn main() {
let with_unknown: u8 = 0b1111;
// Strict: fails on unknown bits
match deserialize_config(with_unknown, true) {
Ok(c) => println!("Strict: {:?}", c),
Err(e) => println!("Strict error: {}", e),
}
// Lenient: preserves unknown bits
match deserialize_config(with_unknown, false) {
Ok(c) => println!("Lenient: {:?} = {:08b}", c, c.bits()),
Err(e) => println!("Lenient error: {}", e),
}
// Round-trip with lenient deserialization
let original = Config::from_bits_retain(with_unknown);
let serialized = serialize_config(original);
let deserialized = deserialize_config(serialized, false).unwrap();
println!("Round-trip preserved: {}", original == deserialized);
println!("Bits preserved: {:08b}", deserialized.bits());
}Choose from_bits for strict validation, from_bits_retain for maximum compatibility.
Method comparison:
| Method | Unknown Bits | Return Type | Use Case |
|--------|--------------|-------------|----------|
| from_bits | Reject | Option<Flags> | Strict validation |
| from_bits_truncate | Remove | Flags | Known flags only |
| from_bits_retain | Preserve | Flags | Interoperability |
| bits | Return all | Bits | Raw value access |
Bits access methods:
| Method | Description |
|--------|-------------|
| bits() | Returns raw bits (may include unknown) |
| all().bits() | Returns bits for all defined flags |
| intersection(all).bits() | Returns only known bits |
When to use each method:
| Scenario | Method |
|----------|--------|
| Parsing external data (strict) | from_bits |
| Parsing external data (preserving) | from_bits_retain |
| Cleaning user input | from_bits_truncate |
| Hardware interoperability | from_bits_retain |
| Round-trip serialization | from_bits_retain |
| Display/debug known flags | bits() with validation |
Key operations for unknown bits:
// Check if unknown bits present
fn has_unknown(flags: Flags) -> bool {
flags.bits() != flags.intersection(Flags::all()).bits()
}
// Extract unknown bits
fn unknown_bits(flags: Flags) -> Bits {
flags.bits() & !Flags::all().bits()
}
// Remove unknown bits
fn only_known(flags: Flags) -> Flags {
Flags::from_bits_truncate(flags.bits())
}Key insight: bits() and from_bits_retain() form a complementary pair for lossless bit manipulationāthe from_bits_retain() constructor accepts any bits value including unknown bits, while bits() returns all bits including unknowns, enabling round-trip preservation. This is essential when interfacing with external systems (file formats, hardware registers, network protocols) that may define additional bits beyond your flag definitions. In contrast, from_bits() provides strict validation (rejecting unknown bits) and from_bits_truncate() sanitizes input (removing unknown bits), both of which are appropriate when you want to ensure only known flags are processed. The choice between these methods represents a fundamental trade-off: from_bits_retain preserves all information but may propagate unexpected bits, while from_bits and from_bits_truncate ensure known flags at the cost of information loss.