Loading pageā¦
Rust walkthroughs
Loading pageā¦
bitflags::Flags::from_bits_truncate for safe flag construction from unknown values?from_bits_truncate constructs a flags value from raw bits by discarding any unknown bits, ensuring the resulting value contains only valid flags defined in the enum. This provides a safe middle ground between from_bits (which returns None for unknown bits) and from_bits_unchecked (which accepts any bits without validation). When processing external inputāconfiguration files, network protocols, or user dataābit values may contain undefined flags from newer versions, corrupted data, or simple errors. from_bits_truncate strips unknown bits rather than failing, allowing forward-compatible parsing where unknown flags are silently ignored rather than causing errors.
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy)]
struct Permissions: u32 {
const READ = 0b00000001;
const WRITE = 0b00000010;
const EXECUTE = 0b00000100;
const ADMIN = 0b00001000;
}
}
fn main() {
// Valid flags
let perms = Permissions::READ | Permissions::WRITE;
println!("{:?}", perms); // READ | WRITE
// Check what bits are set
println!("bits: {:b}", perms.bits()); // bits: 11
}bitflags! macro creates a struct with associated constants representing individual flags.
use bitflags::bitflags;
bitflags! {
struct Permissions: u32 {
const READ = 0b00000001;
const WRITE = 0b00000010;
const EXECUTE = 0b00000100;
}
}
fn main() {
// Scenario: External input contains unknown bits
// Maybe from a newer version of the protocol, or corrupt data
let external_value: u32 = 0b00001111; // Includes bit 3 (value 8)
// This has READ (1), WRITE (2), EXECUTE (4), and UNKNOWN bit (8)
// Problem: from_bits returns None for unknown bits
let result = Permissions::from_bits(external_value);
println!("from_bits: {:?}", result); // None
// The valid flags (READ, WRITE, EXECUTE) are lost
// because the entire value is rejected
}from_bits rejects values containing any bits not defined as flags, losing valid data.
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
struct Permissions: u32 {
const READ = 0b00000001;
const WRITE = 0b00000010;
const EXECUTE = 0b00000100;
}
}
fn main() {
let external_value: u32 = 0b00011111;
// Bits set: 1, 2, 4, 8, 16
// Known flags: READ (1), WRITE (2), EXECUTE (4)
// Unknown bits: 8, 16
// from_bits_truncate extracts only known flags
let truncated = Permissions::from_bits_truncate(external_value);
println!("Truncated: {:?}", truncated);
// Truncated: READ | WRITE | EXECUTE
// Unknown bits are silently discarded
println!("Truncated bits: {:b}", truncated.bits());
// Truncated bits: 111 (only bits 1, 2, 4)
}from_bits_truncate preserves known flags while discarding unknown bits.
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
struct Flags: u8 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
}
}
fn main() {
let known_bits: u8 = 0b00000101; // A | C - all valid
let unknown_bits: u8 = 0b00001101; // A | C | UNKNOWN (bit 3)
// from_bits: Strict validation
println!("from_bits (known): {:?}", Flags::from_bits(known_bits));
// Some(A | C)
println!("from_bits (unknown): {:?}", Flags::from_bits(unknown_bits));
// None - rejects entire value
// from_bits_truncate: Lenient extraction
println!("from_bits_truncate (known): {:?}", Flags::from_bits_truncate(known_bits));
// A | C
println!("from_bits_truncate (unknown): {:?}", Flags::from_bits_truncate(unknown_bits));
// A | C - unknown bit discarded
// from_bits_unchecked: Unsafe, no validation
println!("from_bits_unchecked (unknown): {:?}", unsafe {
Flags::from_bits_unchecked(unknown_bits)
});
// A | C | <unknown bits> - includes undefined bits
}Each method handles unknown bits differently: reject, truncate, or include.
use bitflags::bitflags;
use std::str::FromStr;
bitflags! {
#[derive(Debug)]
struct ConfigFlags: u32 {
const DEBUG = 0b00000001;
const VERBOSE = 0b00000010;
const LOG_FILE = 0b00000100;
const DAEMON = 0b00001000;
}
}
fn parse_config_from_file(content: &str) -> ConfigFlags {
// Parse hex or decimal flags from config file
let bits: u32 = u32::from_str_radix(content.trim(), 16)
.unwrap_or(0);
// Use from_bits_truncate to handle:
// - Unknown flags from newer config versions
// - Typos or corruption in config file
// - Reserved bits that shouldn't cause errors
ConfigFlags::from_bits_truncate(bits)
}
fn main() {
// Config file might have flags from newer version
let newer_config = "0xFF"; // All bits set
let flags = parse_config_from_file(newer_config);
println!("Parsed flags: {:?}", flags);
// DEBUG | VERBOSE | LOG_FILE | DAEMON
// Unknown bits 0xFFFFFFF0 discarded
// Safe parsing
let corrupted = "0xDEADBEEF";
let flags = parse_config_from_file(corrupted);
println!("Corrupted config: {:?}", flags);
// Still returns valid subset of flags
}from_bits_truncate enables robust parsing of external data that may contain unknown bits.
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy)]
struct PacketFlags: u16 {
// Version 1.0 flags
const COMPRESSED = 0x0001;
const ENCRYPTED = 0x0002;
const PRIORITY = 0x0004;
// Version 2.0 added (not known in v1 client)
// const STREAMING = 0x0008;
// const FRAGMENTED = 0x0010;
}
}
struct Packet {
flags: PacketFlags,
data: Vec<u8>,
}
impl Packet {
fn parse(header: &[u8]) -> Result<Self, &'static str> {
if header.len() < 2 {
return Err("Header too short");
}
// Read flags from network byte order
let raw_flags = u16::from_be_bytes([header[0], header[1]]);
// Use from_bits_truncate for forward compatibility:
// - Server v2.0 sends STREAMING | FRAGMENTED flags
// - Client v1.0 doesn't know these flags
// - Truncation allows client to parse packet anyway
let flags = PacketFlags::from_bits_truncate(raw_flags);
Ok(Packet {
flags,
data: vec![],
})
}
}
fn main() {
// Server v2.0 sends: COMPRESSED | PRIORITY | STREAMING (new)
let v2_header = [0x00, 0x0D]; // 0b00001101
let packet = Packet::parse(&v2_header).unwrap();
println!("Parsed flags: {:?}", packet.flags);
// COMPRESSED | PRIORITY
// STREAMING (0x08) truncated - not defined in this version
// Client can still process known flags
if packet.flags.contains(PacketFlags::COMPRESSED) {
println!("Decompressing data...");
}
}Forward compatibility: older clients gracefully handle packets from newer protocol versions.
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy)]
struct UserPermissions: i32 {
const READ = 1;
const WRITE = 2;
const DELETE = 4;
const ADMIN = 8;
}
}
struct User {
id: i32,
name: String,
permissions: UserPermissions,
}
impl User {
fn from_database_row(row: &Row) -> Self {
let id: i32 = row.get(0);
let name: String = row.get(1);
let raw_perms: i32 = row.get(2);
// Database might contain:
// - Permissions from previous application version
// - Manually edited values
// - Migration artifacts
//
// from_bits_truncate ensures only valid permissions
User {
id,
name,
permissions: UserPermissions::from_bits_truncate(raw_perms),
}
}
fn to_database(&self) -> i32 {
// Store only defined bits
self.permissions.bits()
}
}
// Mock Row type
struct Row;
impl Row {
fn get<T>(&self, _idx: usize) -> T
where T: std::str::FromStr + Default
{
T::default()
}
}
fn main() {
// Suppose database has invalid permission bits
let raw_from_db: i32 = 0b11111111; // All bits set
let user_perms = UserPermissions::from_bits_truncate(raw_from_db);
println!("Loaded permissions: {:?}", user_perms);
// READ | WRITE | DELETE | ADMIN
// Unknown bits discarded
// Only valid bits are stored back
println!("Bits to store: {:b}", user_perms.bits());
// 1111 - only known flags
}Database values can contain historical or invalid bits; from_bits_truncate sanitizes them.
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
struct FeatureFlags: u32 {
const FEATURE_A = 0b0001;
const FEATURE_B = 0b0010;
const FEATURE_C = 0b0100;
}
}
fn process_with_flags(raw_flags: u32) {
// Get valid subset
let flags = FeatureFlags::from_bits_truncate(raw_flags);
// Check for unknown bits
if flags.bits() != raw_flags {
println!("Warning: {} unknown bits discarded",
(raw_flags & !flags.bits()).count_ones());
}
// Proceed with known flags
if flags.contains(FeatureFlags::FEATURE_A) {
println!("Feature A enabled");
}
if flags.contains(FeatureFlags::FEATURE_B) {
println!("Feature B enabled");
}
}
fn main() {
// Known flags only
process_with_flags(0b0111);
// Feature A enabled
// Feature B enabled
// Warning: 0 unknown bits discarded
// Mixed known and unknown
process_with_flags(0b1111);
// Feature A enabled
// Feature B enabled
// Warning: 1 unknown bits discarded
}Compare truncated bits to original to detect and log unknown bits.
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
struct Flags: u8 {
const A = 1;
const B = 2;
const C = 4;
}
}
fn main() {
// all() returns all defined flags
let all_flags = Flags::all();
println!("All: {:?}", all_flags); // A | B | C
// empty() returns no flags
let no_flags = Flags::empty();
println!("Empty: {:?}", no_flags); // (empty)
// all() is guaranteed to have only defined bits
// from_bits_truncate(all().bits()) always succeeds:
let reconstructed = Flags::from_bits_truncate(Flags::all().bits());
assert!(reconstructed == Flags::all());
// all() vs from_bits_truncate with all bits set:
let all_bits: u8 = 0xFF;
let truncated = Flags::from_bits_truncate(all_bits);
println!("Truncated 0xFF: {:?}", truncated); // A | B | C
// truncated == all(), unknown bits stripped
assert!(truncated == Flags::all());
}all() gives all defined flags; from_bits_truncate extracts the same from any value.
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy)]
struct Permissions: u32 {
const READ = 0b0001;
const WRITE = 0b0010;
const EXECUTE = 0b0100;
}
}
fn main() {
// Union (|) combines flags
let combined = Permissions::READ | Permissions::WRITE;
println!("Combined: {:?}", combined); // READ | WRITE
// Intersection (&) keeps common flags
let other = Permissions::WRITE | Permissions::EXECUTE;
let common = combined & other;
println!("Common: {:?}", common); // WRITE
// with from_bits_truncate:
let raw_a: u32 = 0b0111; // All three + unknown
let raw_b: u32 = 0b1010; // WRITE + unknown
let perm_a = Permissions::from_bits_truncate(raw_a);
let perm_b = Permissions::from_bits_truncate(raw_b);
let union = perm_a | perm_b;
println!("Union: {:?}", union); // READ | WRITE | EXECUTE
// Only valid flags participate in operations
}Operations on flags always work with defined bits only, regardless of source.
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Default)]
struct Options: u32 {
const OPT_A = 0b0001;
const OPT_B = 0b0010;
// Note: No OPT_C = 0b0100
}
}
impl std::fmt::Display for Options {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut flags = Vec::new();
if self.contains(Options::OPT_A) {
flags.push("A");
}
if self.contains(Options::OPT_B) {
flags.push("B");
}
write!(f, "{}", flags.join(" | "))
}
}
fn main() {
// Default gives empty flags
let default_opts = Options::default();
println!("Default: {:?}", default_opts); // (empty)
// from_bits_truncate with default bits
let zero_opts = Options::from_bits_truncate(0);
assert!(zero_opts == Options::default());
// Display shows only valid flags
let opts = Options::from_bits_truncate(0b1111);
println!("Display: {}", opts); // A | B
// Unknown bits don't affect display
// because they're truncated during construction
}from_bits_truncate ensures Display and Debug outputs only show defined flags.
use bitflags::bitflags;
use std::fs;
bitflags! {
#[derive(Debug, Clone, Copy, Default)]
struct ServerConfig: u32 {
const SSL = 1;
const GZIP = 2;
const CACHE = 4;
const LOG_REQUESTS = 8;
}
}
fn load_config(path: &str) -> ServerConfig {
// Read config file
let contents = match fs::read_to_string(path) {
Ok(s) => s,
Err(_) => return ServerConfig::default(),
};
// Parse flags from config
// Config might be from newer version, manually edited, etc.
let bits: u32 = contents
.trim()
.parse()
.unwrap_or(0);
// Safe extraction with truncation
ServerConfig::from_bits_truncate(bits)
}
fn main() {
// Simulate config parsing
let config_bits = "15"; // 0b1111 - all defined flags
let config = ServerConfig::from_bits_truncate(
config_bits.parse().unwrap()
);
println!("Config: {:?}", config);
// SSL | GZIP | CACHE | LOG_REQUESTS
// Unknown bits in config
let unknown_bits = "255"; // 0b11111111
let config2 = ServerConfig::from_bits_truncate(
unknown_bits.parse().unwrap()
);
println!("Config2: {:?}", config2);
// SSL | GZIP | CACHE | LOG_REQUESTS
// Unknown bits 0b11110000 discarded
}Config files benefit from truncation: future versions add flags, older versions truncate them.
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
struct StatusRegister: u32 {
// Known status bits
const READY = 0b0001;
const BUSY = 0b0010;
const ERROR = 0b0100;
const INTERRUPT = 0b1000;
// Hardware revision 2.0 might have additional bits
// that we don't know about
}
}
fn read_hardware_status() -> u32 {
// Simulated hardware register read
// In real code: memory-mapped I/O
0b11111111 // All bits set, some unknown
}
fn check_status() {
let raw_status = read_hardware_status();
// Truncate to known flags
let status = StatusRegister::from_bits_truncate(raw_status);
println!("Raw: 0x{:X}", raw_status);
println!("Status: {:?}", status);
// Process known status bits
if status.contains(StatusRegister::ERROR) {
println!("Hardware error detected!");
}
if status.contains(StatusRegister::READY) {
println!("Hardware ready");
}
// Unknown bits (0xF0) are safely ignored
}
fn main() {
check_status();
// Raw: 0xFF
// Status: READY | BUSY | ERROR | INTERRUPT
// Hardware error detected!
// Hardware ready
}Hardware registers often have reserved or version-specific bits; truncation handles unknown bits gracefully.
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
struct Flags: u8 {
const A = 1;
const B = 2;
}
}
fn main() {
// Comparison of construction methods:
// | Method | Input | Output | Unknown Bits |
// |--------|-------|--------|--------------|
// | from_bits | 0b11 | Some(A \| B) | None |
// | from_bits | 0b111 | None | Rejected |
// | from_bits_truncate | 0b11 | A \| B | Ignored |
// | from_bits_truncate | 0b111 | A \| B | Stripped |
// | from_bits_unchecked | 0b11 | A \| B | N/A |
// | from_bits_unchecked | 0b111 | A \| B \| unknown | Included (unsafe) |
// When to use each:
// - from_bits: When unknown bits indicate corruption
// - from_bits_truncate: When unknown bits should be ignored
// - from_bits_unchecked: When you're certain bits are valid (unsafe)
let known = 0b11u8;
let unknown = 0b111u8;
println!("from_bits(known): {:?}", Flags::from_bits(known));
println!("from_bits(unknown): {:?}", Flags::from_bits(unknown));
println!("from_bits_truncate(unknown): {:?}", Flags::from_bits_truncate(unknown));
}Choose the construction method based on how strictly you need to validate input.
Purpose of from_bits_truncate:
// Creates flags from raw bits, discarding unknown bits
let flags = Flags::from_bits_truncate(raw_value);
// Guaranteed to contain only defined flags
// Unknown bits silently removedKey characteristics:
| Method | Returns | Unknown Bits | Use Case |
|--------|---------|--------------|----------|
| from_bits | Option<Flags> | Returns None | Strict validation |
| from_bits_truncate | Flags | Discarded | Forward compatibility |
| from_bits_unchecked | Flags | Preserved | Performance (unsafe) |
When to use from_bits_truncate:
When not to use:
from_bits)from_bits)from_bits_unchecked)Key insight: from_bits_truncate provides graceful degradation when processing data from untrusted or version-mismatched sources. It extracts the known subset of flags and discards unknown bits, enabling forward compatibility where older code handles newer data without errors. This is particularly valuable for configuration systems, network protocols, and database schemas where the flag definitions evolve over time but older clients should still function with whatever flags they recognize.