Expand description
The bitflags-lite crate provides a lightweight, macro-free, generic
bitflags implementation for Rust.
BitFlags<T, U> is a #[repr(transparent)] wrapper around an integer T
that adds a compile-time tag type U.
The tag distinguishes flag sets that share the same storage type and
describes which bits are valid through the BitFlagsSpec trait.
It is no_std and no_alloc compatible, and const-friendly.
§Why a generic BitFlags<T, U>?
Macro-based bitflags crates generate a distinct struct for every flag set.
Each invocation re-expands the full method set, and the resulting types
share no common trait or type with one another.
By contrast, bitflags-lite defines BitFlags<T, U> once and asks you to
specialize it through a plain trait implementation.
That design has a few concrete consequences:
- Generic code over flag sets. Because every flag set is the same type
family, you can write
fn f<T, U: BitFlagsSpec<T>>(flags: BitFlags<T, U>). This is not possible with macro-generated structs. - Predictable expansion. A new flag set adds a small trait impl, not a fresh copy of the entire API.
- Inspectable source. The spec is ordinary Rust.
rustdocrenders it,rust-analyzernavigates it,grepfinds it.
But you have to give up automatic derivation of flag names and valid bits
and write BitFlagsSpec::VALID_BITS_MASK and BitFlagsSpec::FLAG_NAMES
by hand and keep them in sync with your constants.
§When to choose bitflags, enumflags2, or bitflags-lite
- Choose
bitflagsif you want the mature and battle-tested crate, the ecosystem-standard macro, multi-bit convenience flags, and unnamed wildcard flags. - Choose
enumflags2if you want a single-flag type distinct from the set type, enum-variant-driven definitions, and match/iter over variants. - Choose
bitflags-liteif you want a lightweight flags definition with no macros and a reusable genericBitFlags<T, U>, are comfortable with a per-bit naming model, and you don’t need combined or multi-bit flags.
§Usage
[dependencies]
bitflags-lite = "0.1.0"With optional features:
[dependencies]
bitflags-lite = { version = "0.1.0", features = [ "serde", "bytemuck" ] }§Examples
Define a tag type and implement BitFlagsSpec for it:
use core::str::FromStr;
use bitflags_lite::{BitFlag, BitFlags, BitFlagsSpec, NameMatcher};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
struct PermissionsTag;
impl BitFlagsSpec<u32> for PermissionsTag {
const VALID_BITS_MASK: u32 = (1 << 3) - 1;
const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::Exact;
}
type Permission = BitFlag<u32, PermissionsTag>;
type Permissions = BitFlags<u32, PermissionsTag>;
// Constructors are `const` for concrete integer types.
const READ: Permission = Permission::try_from_bit(0).unwrap();
const WRITE: Permission = Permission::try_from_bit(1).unwrap();
const EXEC: Permission = Permission::try_from_bit(2).unwrap();
let rwx = READ | WRITE | EXEC;
assert!(rwx.contains(READ.into()));
assert!(rwx.contains(WRITE.into()));
assert!(rwx.contains(EXEC.into()));
assert!(rwx.contains(READ | WRITE));
let read_exec = READ | EXEC;
assert!(!read_exec.contains(WRITE.into()));
let flags = Permissions::from_str("read | 0x18 | write").unwrap();
assert_eq!(format!("{flags}"), "read | write | 0x18");
let flags: Vec<Permission> = flags.into_iter().collect();
assert_eq!(
flags,
[
READ,
WRITE,
Permission::from_bit_retain(3),
Permission::from_bit_retain(4),
]
);
let formatted: Vec<String> = flags.iter().map(ToString::to_string).collect();
assert_eq!(formatted, ["read", "write", "0x8", "0x10"]);
let named_only: Vec<&'static str> = flags.iter().filter_map(BitFlag::name).collect();
assert_eq!(named_only, ["read", "write"]);Constants can also be defined through an inherent implementation, a trait,
or an associated const on the tag type — whatever fits your code style.
The tag type keeps flag sets distinct even when they share a storage type, providing type-safety across otherwise-similar types:
The following example deliberately fails to compile:
use bitflags_lite::{BitFlags, BitFlagsSpec, NameMatcher};
struct PermsTag;
type Perms = BitFlags<u8, PermsTag>;
impl BitFlagsSpec<u8> for PermsTag {
const VALID_BITS_MASK: u8 = (1 << 3) - 1;
const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::AsciiCaseInsensitive;
}
struct KeyboardModifiersTag;
type KeyboardModifiers = BitFlags<u8, KeyboardModifiersTag>;
impl BitFlagsSpec<u8> for KeyboardModifiersTag {
const VALID_BITS_MASK: u8 = (1 << 4) - 1;
const FLAG_NAMES: &[&[&str]] = &[&["Shift"], &["Ctrl"], &["Alt"], &["Meta"]];
const DEFAULT_NAME_MATCHER: NameMatcher = NameMatcher::AsciiCaseInsensitive;
}
let rwx: Perms = Perms::try_from_bits(0b111).unwrap();
let keyboard_modifiers: KeyboardModifiers = rwx;
// ^^^
// ❌ error[E0308]: mismatched types
// expected struct `BitFlags<_, KeyboardModifiersTag>`
// found struct `BitFlags<_, test::PermsTag>`§Design Highlights
- Single-flag vs. set distinction.
BitFlag<T, U>represents one bit index.BitFlags<T, U>represents a set. They convert into each other and interoperate through the usual operators. - Invalid bits are representable. A
BitFlagsvalue may contain bits not set inBitFlagsSpec::VALID_BITS_MASK; they are preserved by raw-bit operations and cleared byBitFlags::truncate,BitFlags::complement, andNot. UseBitFlags::is_validto check. - Const-friendly. Every constructor, accessor, and combinator on the
concrete integer types is a
const fn. The generic equivalents live in theBitFlagsExtandBitFlagExtextension traits. - No allocation. Parsing and formatting work without
alloc. Theallocfeature only enriches error messages with the offending input.
§Comparison with Existing Crates
| Aspect | bitflags-lite | bitflags v2 | enumflags2 v0.7 |
|---|---|---|---|
| Definition mechanism | Trait BitFlagsSpec<T> for a tag type | bitflags! { ... } macro | #[bitflags] attribute macro |
| Generated/used type | BitFlags<T, U> over a tag type U | User-owned struct | BitFlags<T, N = T::Numeric> over an enum |
| Feature | bitflags-lite | bitflags v2 | enumflags2 v0.7 |
|---|---|---|---|
| Automatically deduced flag names | ⛔️ No | ✅ Yes | ✅ Yes |
| Automatically deduced valid bits | ⛔️ No | ✅ Yes | ✅ Yes |
| Automatically sync names and valid bits | ⛔️ No | ✅ Yes | ✅ Yes |
| Most methods const | ✅ Yes | ✅ Yes | ⚠️ Limited |
| Multi-bit flags | ⛔️ No | ✅ Yes | ⛔️ No |
| Single-flag vs set distinction | ✅ Yes | ⛔️ No | ✅ Yes |
| Alias names for parsing | ✅ Yes | ⛔️ No | ⛔️ No |
| Rename a flag | ✅ Yes | ✅ Yes | ⛔️ No |
| Custom default | ⛔️ No | ✅ Yes | ✅ Yes |
| Only valid bits | ⛔️ No | ⛔️ No | ✅ Yes |
| Are invalid bits allowed? | ✅ Yes | ✅ Yes | ⛔️ No |
| Format with names | ✅ Yes | ✅ Yes | ✅ Yes |
| Parse from names | ✅ Yes | ✅ Yes | ⛔️ No |
| Case-insensitive parsing | ✅ Yes | ⛔️ No | ⛔️ No |
§Documentation
§Feature flags
alloc(implied bystd, enabled by default): enriches parse errors with the offending input.std(enabled by default): enablesallocfeature.arbitrary: implementsArbitraryforBitFlag<T, U>andBitFlags<T, U>.serde: implementsserde::Serializeandserde::DeserializeforBitFlag<T, U>andBitFlags<T, U>round-tripping through flag strings.bytemuck: implementsZeroableandPodforBitFlag<T, U>andBitFlags<T, U>, allowing them to be used in plain-old-data contexts.
§License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
§Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Modules§
Structs§
- BitFlag
- A single named or indexed bit in a
BitFlagsset. - BitFlag
From Flags Error - An error returned when converting
BitFlagsinto aBitFlagfails. - BitFlag
Parse Error - An error encountered while parsing flags from text.
- BitFlag
Parse HexError - An error returned when parsing a hexadecimal flag fails.
- BitFlag
Parse Name Error - An error returned when parsing a named flag fails.
- BitFlags
- A type-safe set of flags stored in an integer
T. - BitFlags
Iter - An iterator over the individual bits set in a
BitFlagsvalue. - BitFlags
Parse Error - An error encountered while parsing flags from text.
Enums§
- Name
Matcher - Controls how flag names are matched when parsing.
Traits§
- BitFlag
Ext - Extension trait providing generic, non-const equivalents of the
type-specific inherent methods on
BitFlag. - BitFlags
Ext - Extension trait providing generic, non-const equivalents of the
type-specific inherent methods on
BitFlags. - BitFlags
Spec - Associates a bit-storage type
Twith the set of bits that are valid for a particularBitFlagstag.