bitflags-lite: Lightweight generics-based bitflags
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
[]
= "0.1.0"
With optional features:
[]
= { = "0.1.0", = [ "serde", "bytemuck" ] }
Examples
Define a tag type and implement BitFlagsSpec for it:
use FromStr;
use ;
;
type Permission = ;
type Permissions = ;
// Constructors are `const` for concrete integer types.
const READ: Permission = try_from_bit.unwrap;
const WRITE: Permission = try_from_bit.unwrap;
const EXEC: Permission = try_from_bit.unwrap;
let rwx = READ | WRITE | EXEC;
assert!;
assert!;
assert!;
assert!;
let read_exec = READ | EXEC;
assert!;
let flags = from_str.unwrap;
assert_eq!;
let flags: = flags.into_iter.collect;
assert_eq!;
let formatted: = flags.iter.map.collect;
assert_eq!;
let named_only: = flags.iter.filter_map.collect;
assert_eq!;
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 ;
;
type Perms = ;
;
type KeyboardModifiers = ;
let rwx: Perms = try_from_bits.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 in [BitFlagsSpec::VALID_BITS_MASK]; they are preserved by raw-bit operations and cleared by [BitFlags::truncate], [BitFlags::complement], andNot. Use [BitFlags::is_valid] to 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.