# bitflags-lite: Lightweight generics-based bitflags
[](https://github.com/zheland/bitflags-lite/actions)
[](https://crates.io/crates/bitflags-lite)
[](https://docs.rs/bitflags-lite)
[](https://codecov.io/gh/zheland/bitflags-lite)
[](https://deps.rs/repo/github/zheland/bitflags-lite)
[](https://crates.io/crates/bitflags-lite)
[](https://github.com/zheland/bitflags-lite/#license)
[](https://blog.rust-lang.org/2026/07/09/Rust-1.97.0/)
The `bitflags-lite` crate provides a lightweight, 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.
Flag sets can be defined either through the [`bitflags!`] macro, which
generates the trait implementation and named constants from a compact
declaration, or by implementing [`BitFlagsSpec`] directly.
Both forms produce the same types and the same runtime behavior.
The crate is `no_std` and `no_alloc` compatible, and const-friendly.
## When to choose `bitflags`, `bitflags-lite`, or `enumflags2`
- Choose [`bitflags`] if you want the mature and battle-tested crate, and
the ecosystem-standard macro, and the ability to extend each flag set with
inherent methods or third-party trait implementations.
- Choose [`bitflags-lite`] if you want a reusable generic [`BitFlags<T, U>`]
with a small, inspectable expansion, optionally generated by a concise
macro, and you don't need the ability to extend each flag set with
inherent methods or third-party trait implementations.
- Choose [`enumflags2`] if you want a single-flag type distinct from the set
type, enum-variant-driven definitions, and match/iter over variants.
## About
Macro-based [`bitflags`] crate 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.
The provided [`bitflags!`] macro is a convenience wrapper that generates
that trait impl and the named constants — it does not generate a new type.
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 [`BitFlags`] value may contain bits
not set in [`BitFlagsSpec::VALID_BITS_MASK`]; they are preserved by
raw-bit operations and cleared by [`BitFlags::truncate`],
[`BitFlags::complement`], and [`Not`](core::ops::Not). 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
the [`BitFlagsExt`] and [`BitFlagExt`] extension traits.
- **No allocation.** Parsing and formatting work without `alloc`. The
`alloc` feature only enriches error messages with the offending input.
- **No hidden type generation.** Even with the [`bitflags!`] macro, no new
type is created per flag set. The macro builds on the same generic
[`BitFlags<T, U>`], so all flag sets remain one type family.
## Usage
```toml
[dependencies]
bitflags-lite = "0.3.0"
```
With optional features:
```toml
[dependencies]
bitflags-lite = { version = "0.3.0", features = [ "serde", "bytemuck" ] }
```
## Examples
### With the [`bitflags!`] macro
The macro generates the tag type, the flag-set alias, the [`BitFlagsSpec`]
implementation, and the named constants from one compact declaration:
```rust
use core::str::FromStr;
use bitflags_lite::{BitFlag, BitFlags, BitFlagsSpec, NameMatcher};
bitflags_lite::bitflags! {
#![storage(u32)]
pub struct PermissionsTag;
pub bit READ = 0, &["read"];
pub bit WRITE = 1, &["write"];
pub bit EXEC = 2, &["exec", "execute"];
pub mask RWX = [READ, WRITE, EXEC], "rwx";
pub mask NONE = 0, "none";
}
pub type Permission = BitFlag<u32, PermissionsTag>;
pub type Permissions = BitFlags<u32, PermissionsTag>;
assert!(rwx.contains(WRITE.into()));
assert!(rwx.contains(EXEC.into()));
assert!(rwx.contains(RWX));
let read_exec = READ | EXEC;
assert!(!read_exec.contains(WRITE.into()));
// Combined names participate in parsing.
let parsed = Permissions::from_str("rwx").unwrap();
assert_eq!(format!("{parsed}"), "read | write | exec");
let parsed = Permissions::from_str("read | 0x18 | execute").unwrap();
assert_eq!(format!("{parsed}"), "read | exec | 0x18");
// Iteration yields individual BitFlag values.
let flags: Vec<Permission> = parsed.into_iter().collect();
assert_eq!(
flags,
[
READ,
EXEC,
BitFlag::from_bit_retain(3),
BitFlag::from_bit_retain(4),
]
);
let formatted: Vec<String> = flags.iter().map(ToString::to_string).collect();
assert_eq!(formatted, ["read", "exec", "0x8", "0x10"]);
let named_only: Vec<&'static str> = flags.iter().filter_map(BitFlag::name).collect();
assert_eq!(named_only, ["read", "exec"]);
```
### Without the macro
The same flag set can be written manually.
This is what the macro expands to, and it is fully supported as a public
API:
```rust
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 = 0b111;
const FLAG_NAMES: &[&[&str]] = &[&["read"], &["write"], &["exec", "execute"]];
const COMBINED_NAMES: &[(u32, &str)] = &[(0, "none"), (0b111, "rwx")];
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();
const RWX: Permissions = Permissions::try_from_bits(0b111).unwrap();
```
Constants can also be defined through an inherent implementation, a trait,
or an associated `const` on the tag type.
### Type-safety across flag sets
The tag type keeps flag sets distinct even when they share a storage type.
The following example deliberately fails to compile:
```rust,compile_fail
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 COMBINED_NAMES: &[(u8, &str)] = &[(0, "none"), (0b111, "rwx")];
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 COMBINED_NAMES: &[(u8, &str)] = &[];
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>`
```
## Comparison with Existing Crates
### Definition
| Defined by trait impl | ✅ `BitFlagsSpec<T>` | ⛔️ No | ⛔️ No |
| Defined by macro | ✅ `bitflags!` | ✅ `bitflags!` | ⛔️ No |
| Defined by attribute | ⛔️ No | ⛔️ No | ✅ `#[bitflags(...)]` |
| Generates a distinct new type | ⛔️ No (generic over `U`) | ✅ Yes | ⛔️ No (uses user enum) |
### Type model
| Single-flag vs. set distinction | ✅ `BitFlag` / `BitFlags` | ⛔️ No | ✅ `T` / `BitFlags` |
| Iteration yields | `BitFlag<T, U>` | `Flags` (self, one bit) | `enum T` (variant) |
| Match exhaustively on single flag | ⛔️ No | ⛔️ No | ✅ `match flag { T::A => ... }` |
| Generic over a single flag | ✅ `BitFlag<T, U>` | ⛔️ No | ✅ `enum T` |
| Generic over flag sets | ✅ `BitFlags<T, U>` | ⛔️ No | ✅ `BitFlags<T, N>` |
### Extension
| Inherent methods | ⛔️ No | ✅ Yes | ⛔️ No |
| Third-party trait impls | ⛔️ No | ✅ Yes | ⛔️ No |
| Local trait impls | ✅ Yes | ✅ Yes | ✅ Yes |
### Capabilities
| Most methods const | ✅ Integer impls | ✅ Targeted codegen | ⚠️ Limited |
| Multi-bit flags | ⚠️ Parse-only | ✅ Format and parse | ⛔️ No |
| Custom display name | ✅ Yes | ✅ `#[name]` | ⛔️ No |
| Alias names for parsing | ✅ Yes | ⛔️ No | ⛔️ No |
| Format with names | ✅ Yes | ✅ Yes | ✅ Yes |
| Parse from names | ✅ Yes | ✅ Yes | ⛔️ No |
| Case-insensitive parsing | ✅ Configurable | ⛔️ No | ⛔️ No |
| Custom default value | ⛔️ No | ✅ `#[default]` | ✅ `#[default]` |
| Only valid bits representable | ⛔️ No | ⛔️ No | ✅ Yes |
| Invalid bits representable | ✅ Yes | ✅ Yes | ⛔️ No |
## Documentation
[API Documentation]
## Feature flags
- `alloc` (implied by `std`, enabled by default): enriches parse errors with
the offending input.
- `std` (enabled by default): enables the `alloc` feature.
- `arbitrary`: implements `Arbitrary` for `BitFlag<T, U>` and `BitFlags<T,
U>`.
- `serde`: implements `serde::Serialize` and `serde::Deserialize` for
`BitFlag<T, U>` and `BitFlags<T, U>`, round-tripping through flag strings.
- `bytemuck`: implements `Zeroable` and `Pod` for `BitFlag<T, U>` and
`BitFlags<T, U>`, allowing them to be used in plain-old-data contexts.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](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.
[API Documentation]: https://docs.rs/bitflags-lite/latest/bitflags-lite/
[`bitflags`]: https://crates.io/crates/bitflags
[`enumflags2`]: https://crates.io/crates/enumflags2
[`bitflags-lite`]: https://crates.io/crates/bitflags-lite
[`bitflags!`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/macro.bitflags.html
[`BitFlag<T, U>`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/struct.BitFlag.html
[`BitFlags<T, U>`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/struct.BitFlags.html
[`BitFlags`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/struct.BitFlags.html
[`BitFlagExt`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/trait.BitFlagExt.html
[`BitFlagsExt`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/trait.BitFlagsExt.html
[`BitFlagsSpec`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/trait.BitFlagsSpec.html
[`BitFlagsSpec::VALID_BITS_MASK`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/trait.BitFlagsSpec.html#associatedconstant.VALID_BITS_MASK
[`BitFlags::truncate`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/struct.BitFlags.html#method.truncate
[`BitFlags::complement`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/struct.BitFlags.html#method.complement
[`BitFlags::is_valid`]: https://docs.rs/bitflags-lite/latest/bitflags_lite/struct.BitFlags.html#method.is_valid