enumflags2

repository·master·Indexed 40 days ago

https://github.com/meithecatte/enumflags2

A Rust crate providing a type-safe bitflags implementation using enums. It uses the #[bitflags] attribute to maintain a strict type-level distinction between individual enum variants and sets of flags (BitFlags<T>), enabling idiomatic use of match and iterators. Features include automatic bit assignment, compile-time safety, and const-compatible APIs via the make_bitflags! macro. Version 0.7.12.

Tokens
5K
Snippets
17
Records
21
Agent score
47%

What's inside enumflags2

  1. How enumflags2 works

    master

    Core Concept

    enumflags2 implements a bitflags data structure by annotating an enum with the #[bitflags] attribute.

    Key Distinction: Unlike other bitflags crates, enumflags2 maintains a strict type-level distinction between a single flag (the enum type itself, e.g., Test) and a set of flags (the BitFlags<T> type, e.g., BitFlags<Test>). This distinction allows you to use idiomatic Rust patterns like match and iter on the flags.

    Key Features:

    • Automatic Bit Assignment: If you don't specify a bit value for an enum variant, enumflags2 automatically picks an unused bit.
    • Compile-time Safety: It detects incorrect bitflag configurations at compile time.
    • Opaque Types: You do not interact with generated types directly; you work exclusively with struct BitFlags<Enum>;.
    • Rich Debugging: The Debug formatter prints both the binary value and the enum variants (e.g., BitFlags(0b1111, [A, B, C, D])).
    use enumflags2::{bitflags, BitFlags};
    
    #[bitflags]
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, PartialEq)]
    enum Test {
        A = 0b0001,
        B = 0b0010,
        C, // Automatically picks an unused bit
        D = 0b1000,
    }
    
    // A set of flags is a BitFlags<Test>
    let flags: BitFlags<Test> = Test::A | Test::B;
  2. Use const-compatible APIs in const contexts

    master

    Because Rust's const trait support is limited, enumflags2 provides specific patterns for usage in const fn or const blocks:

    1. Suffix _c: Functions intended for const use are suffixed with _c (e.g., method_c()).
    2. make_bitflags! macro: This macro is often the best way to handle const and const fn use cases.
    3. Explicit Widths: When writing const fn that operates on BitFlags, you may need to specify the underlying integer width explicitly (e.g., BitFlags<T, u8>) because generic trait bounds like T: BitFlag are not yet fully supported in const contexts.
  3. Implement bitflags with the #[bitflags] attribute

    master

    To use enumflags2, annotate your enum with #[bitflags]. You should also specify a #[repr(...)] (like u8, u16, etc.) to define the underlying integer size.

    Variants can be assigned specific bit values using bitwise notation, or left unassigned to have enumflags2 automatically select a free bit.

    use enumflags2::bitflags;
    
    #[bitflags]
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, PartialEq)]
    enum Test {
        A = 0b0001,
        B = 0b0010,
        C, // Automatically assigned
        D = 0b1000,
    }
  4. Use the #[bitflags] attribute to create bitfield enums

    master

    The enumflags2_derive crate provides the #[bitflags] attribute macro. When applied to an enum, it transforms it into a set of flags that can be used with the BitFlags<T> type.

    To use this attribute, your enum must satisfy these requirements:

    1. Must be an enum: It cannot be a struct or union.
    2. Must have a #[repr(...)] attribute: You must specify an unsigned integer type (e.g., u8, u16, u32, u64, u128) to define the size of the bitfield.
    3. Unit variants only: Each variant must be a unit variant (it cannot contain additional data).
    4. No generics: The enum cannot be generic.
    5. Power-of-two values: If you manually assign discriminants, they must be powers of two. If you do not assign discriminants, the macro will infer them automatically.

    Supported #[repr] types:

    • u8, u16, u32, u64, u128.
    • Note: usize is not supported. Signed types (like i32) are not supported.
    #[bitflags]
    #[repr(u32)]
    enum MyFlags {
        A,
        B,
        C = 4,
    }
  5. Define bitflags using the `#[bitflags]` attribute

    master

    To use enumflags2, annotate an enum with the #[bitflags] attribute. This allows you to use BitFlags<YourEnum> to hold arbitrary combinations of your enum variants within a single integer. You should also specify a #[repr(...)] (e.g., u8, u16, u32) to define the underlying numeric type.

    Unspecified variants in the enum will automatically pick unused bits.

    use enumflags2::{bitflags, BitFlags};
    
    #[bitflags]
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, PartialEq)]
    enum Test {
        A = 0b0001,
        B = 0b0010,
        C, // picks unused bits automatically
        D = 0b1000,
    }
    
    // Combining flags creates a BitFlags instance
    let a_b: BitFlags<Test> = Test::A | Test::B;
  6. Manipulate and query BitFlags

    master

    Once you have a BitFlags<T> type, you can perform standard bitwise and set operations:

    • Combination: Use the bitwise OR operator | between enum variants to create a BitFlags<T> instance, or use the make_bitflags! macro.
    • Iteration: Use .iter() to iterate over the set of active flags.
    • Containment: Use .contains(flags) to check if a specific flag or set of flags is present.
    • Intersection: Use .intersects(flags) to check if there is any overlap between two flag sets.
    • Formatting:
      • {:?} (Debug) shows binary value and variants.
      • {} (Display) shows only the variants (e.g., A | B).
      • {:04b} (Binary) shows the raw bits.
    use enumflags2::{bitflags, make_bitflags, BitFlags};
    
    #[bitflags]
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, PartialEq)]
    enum Test {
        A = 0b0001,
        B = 0b0010,
        C,
        D = 0b1000,
    }
    
    let a_b: BitFlags<Test> = Test::A | Test::B;
    let b_c_d = make_bitflags!(Test::{B | C | D});
    
    // Querying
    assert!(a_b.contains(Test::A));
    assert!(a_b.intersects(Test::A | Test::C));
    assert!(!a_b.intersects(Test::D));
    
    // Iteration
    let collected: Vec<_> = a_b.iter().collect();
    assert_eq!(collected, vec![Test::A, Test::B]);
  7. Customize the Default value for BitFlags

    master

    By default, BitFlags::default() returns an empty set. You can override this behavior by providing a default argument within the #[bitflags] attribute on your enum.

    use enumflags2::bitflags;
    
    #[bitflags(default = B | C)]
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, PartialEq)]
    enum Test {
        A = 0b0001,
        B = 0b0010,
        C = 0b0100,
        D = 0b1000,
    }
    
    // Now default returns B and C
    assert_eq!(BitFlags::<Test>::default(), Test::B | Test::C);
  8. Customize the `Default` implementation for `BitFlags<T>`

    master

    By default, BitFlags::default() returns an empty set (all bits unset). You can customize this behavior by providing a default argument to the #[bitflags] attribute on your enum.

    use enumflags2::{BitFlags, bitflags};
    
    #[bitflags(default = B | C)]
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, PartialEq)]
    enum Test {
        A = 0b0001,
        B = 0b0010,
        C = 0b0100,
        D = 0b1000,
    }
    
    assert_eq!(BitFlags::<Test>::default(), Test::B | Test::C);
  9. Create BitFlags instances with `make_bitflags!`

    master

    The make_bitflags! macro provides a succinct syntax for creating BitFlags<T> instances. It is useful for avoiding repetitive type names and is often usable in const contexts.

    Supported syntaxes:

    • make_bitflags!(Enum::{Variant1 | Variant2})
    • make_bitflags!(Enum::Variant1)
    use enumflags2::{bitflags, BitFlags, make_bitflags};
    
    #[bitflags]
    #[repr(u8)]
    #[derive(Clone, Copy, Debug)]
    enum Test {
        A = 1 << 0,
        B = 1 << 1,
        C = 1 << 2,
    }
    
    // Succinct creation
    let x = make_bitflags!(Test::{A | C});
    
    // Works in const contexts
    const X: BitFlags<Test> = make_bitflags!(Test::A);
  10. Customize the default value using `#[bitflags(default = ...)]`

    master

    You can specify a custom default value for the bitfield by passing a default argument to the #[bitflags] attribute. This allows you to define which flags are enabled by default when using the BitFlags::DEFAULT constant.

    The syntax uses a pipe-separated list of enum variants: default = Variant1 | Variant2.

    #[bitflags(default = A | B)]
    #[repr(u32)]
    enum MyFlags {
        A,
        B,
        C,
    }
    
    // BitFlags<MyFlags>::DEFAULT will now contain both A and B.
  11. Mutate BitFlags using `insert`, `remove`, `toggle`, and `set`

    master

    You can modify a BitFlags instance using the following methods:

    • insert(other): Adds the specified flags.
    • remove(other): Removes the specified flags.
    • toggle(other): Toggles the specified flags.
    • set(other, cond): Inserts the flags if cond is true, otherwise removes them.
    let mut state = Test::A | Test::C;
    
    // Conditional update
    state.set(Test::B, true);  // state is now A | B | C
    state.set(Test::A, false); // state is now B | C
    
    state.insert(Test::D);     // state is now B | C | D
    state.remove(Test::B);     // state is now C | D
    state.toggle(Test::C);     // state is now D