bitflags-lite 0.3.0

Lightweight generics-based bitflags
Documentation
# bitflags-lite: Lightweight generics-based bitflags

[![Build Status](https://github.com/zheland/bitflags-lite/workflows/build/badge.svg)](https://github.com/zheland/bitflags-lite/actions)
[![Latest Version](https://img.shields.io/crates/v/bitflags-lite.svg)](https://crates.io/crates/bitflags-lite)
[![Documentation](https://docs.rs/bitflags-lite/badge.svg)](https://docs.rs/bitflags-lite)
[![Codecov](https://codecov.io/gh/zheland/bitflags-lite/graph/badge.svg)](https://codecov.io/gh/zheland/bitflags-lite)
[![Dependencies status](https://deps.rs/repo/github/zheland/bitflags-lite/status.svg)](https://deps.rs/repo/github/zheland/bitflags-lite)
[![Downloads](https://img.shields.io/crates/d/bitflags-lite)](https://crates.io/crates/bitflags-lite)
[![License](https://img.shields.io/crates/l/bitflags-lite)](https://github.com/zheland/bitflags-lite/#license)
[![MSRV 1.97+](https://img.shields.io/badge/rustc-1.97+-blue.svg)](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>;

let rwx = READ | WRITE | EXEC;
assert!(rwx.contains(READ.into()));
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

| Feature | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
|:--:|:--:|:--:|:--:|
| 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

| Feature | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
|:--:|:--:|:--:|:--:|
| 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

| Feature | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
|:--:|:--:|:--:|:--:|
| Inherent methods | ⛔️ No | ✅ Yes | ⛔️ No |
| Third-party trait impls | ⛔️ No | ✅ Yes | ⛔️ No |
| Local trait impls | ✅ Yes | ✅ Yes | ✅ Yes |

### Capabilities

| Feature | `bitflags-lite` | `bitflags` v2 | `enumflags2` v0.7 |
|:--:|:--:|:--:|:--:|
| 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 |

<!---
  Invalid bits are really not allowed in `enumflags2`.
  Its `from_bits_unchecked` unsafe method requires that
  "All bits set in val must correspond to a value of the enum".
-->


## 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