Crate bitflag_attr

Source
Expand description

Generate types for C-style flags with ergonomic APIs using attribute macros and enums.

§Getting started

Add bitflag_attr to your Cargo.toml:

cargo add bitflag_attr

or

[dependencies]
bitflag-attr = "0.9.0"

§Generating flags type

Use the bitflag attribute macro to generate flag types:

use bitflag_attr::bitflag;

#[bitflag(u32)]
#[derive(Clone, Copy)]
enum Flags {
    A = 0b00000001,
    B = 0b00000010,
    C = 0b00000100
}

Deriving Clone and Copy for the type is mandatory.

The generated type is a struct wrapping the chosen primitive type.

See the docs for the bitflag macro for the full syntax.

Also see the example_generated module for an example of what the bitflag macro generates for a flags type.

§Externally defined flags

If you’re generating flags types for an external source, such as a C API, you can use the non_exhaustive attribute to communicate to the bitflags macro that there may be more valid flags than the known flags.

Without extra configuration, it defaults to !0 (all bits set) as a mask of all bits the external source may ever set, i.e. all bits are considered as possible values.

use bitflag_attr::bitflag;

#[bitflag(u32)]
#[non_exhaustive] // All bits are considered as possible values.
#[derive(Debug, Clone, Copy)]
pub enum Flags {
    /// The value `A`, at bit position `0`.
    A = 0b00000001,
    /// The value `B`, at bit position `1`.
    B = 0b00000010,
    /// The value `C`, at bit position `2`.
    C = 0b00000100,

    /// The combination of `A`, `B`, and `C`.
    ABC = A | B | C,
}

But you can also configure this value by using the helper attribute extra_valid_bits with a desired value of valid bits that the external source may ever set.

use bitflag_attr::bitflag;

#[bitflag(u32)]
#[non_exhaustive] // Communicate there is more potential valid flags than the known flags
#[extra_valid_bits = 0b001001111] // Specify the extra bits to take into consideration.
#[derive(Debug, Clone, Copy)]
pub enum Flags {
    /// The value `A`, at bit position `0`.
    A = 0b00000001,
    /// The value `B`, at bit position `1`.
    B = 0b00000010,
    /// The value `C`, at bit position `2`.
    C = 0b00000100,

    /// The combination of `A`, `B`, and `C`.
    ABC = A | B | C,
}

Why should you do this? Generated methods like all and truncating operators like ! only consider bits in defined flags. Adding an unnamed flag makes those methods consider additional bits, without generating additional constants for them. It helps compatibility when the external source may start setting additional bits at any time. The known and unknown bits section has more details on this behavior.

§Custom derives

You can derive some traits on generated flags types if you enable Cargo features. The following libraries are currently supported:

  • serde: Support #[derive(Serialize, Deserialize)], using text for human-readable formats, and a raw number for binary formats.
  • arbitrary: Support #[derive(Arbitrary)], only generating flags values with known bits.
  • bytemuck: Support #[derive(Pod, Zeroable)], for casting between flags values and their underlying bits values.

§Adding custom methods

The bitflag macro supports any attributes on generated flags types within the macro itself, while impl blocks can be added normally:

#[bitflag(u32)]
// Attributes can be applied to flags types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Flags {
    A = 0b00000001,
    B = 0b00000010,
    C = 0b00000100
}

// Impl blocks can be added to flags types normally
impl Flags {
    pub fn as_u64(&self) -> u64 {
        self.bits() as u64
    }
}

§Working with flags values

Use generated constants and standard bitwise operators to interact with flags values:

// union
let ab = Flags::A | Flags::B;

// intersection
let a = ab & Flags::A;

// difference
let b = ab - Flags::A;

// complement
let c = !ab;

See the docs for the example_generated module and the Flags trait for more details on operators and how they behave.

§Formatting and parsing

bitflags defines a text format that can be used to convert any flags value to and from strings.

See the parser module for more details.

§Terminology

This crate and its documentation tries to follow the same terminology of the bitflags crate (the OG). Here we define some.

§Flags types, flags values, flags

Some terminology to refer to things in the bitflags domain:

  • Bits type: A type that defines a fixed number of bits at specific locations.
  • Flag: A set of bits in a bits type that may have a unique name.
  • Flags type: A set of defined flags over a specific bits type.
  • Flags value: An instance of a flags type using its specific bits value for storage.
#[bitflag(u8)]
//        -- Bits type
#[derive(Clone, Copy)]
enum FlagsType {
//   --------- Flags type
    A = 1
//  ----- Flag
}

let flag = FlagsType::A;
//  ---- Flags value

§Known and unknown bits

Any bits in a flag you define are called known bits. Any other bits are unknown bits. In the following flags type:

#[bitflag(u8)]
#[derive(Clone, Copy)]
enum Flags {
    A = 1,
    B = 1 << 1,
    C = 1 << 2,
}

The known bits are 0b0000_0111 and the unknown bits are 0b1111_1000.

bitflag_attr doesn’t guarantee that a flags value will only ever have known bits set, but some operators will unset any unknown bits they encounter.

If you’re using bitflags for flags types defined externally, such as from C, you probably want all bits to be considered known, in case that external source changes. You can do this using an unnamed flag, as described in externally defined flags.

§Zero-bit flags

Flags with no bits set, in general, should be avoided because they interact strangely with contains and intersects. A zero-bit flag is always contained, but is never intersected. The names of zero-bit flags can be parsed, but are never formatted.

§Multi-bit flags

Flags that set multiple bits should be avoided unless each bit is also in a single-bit flag. Take the following flags type as an example:

#[bitflag(u8)]
#[derive(Clone, Copy)]
enum Flags {
    A = 1,
    B = 1 | (1 << 1),
}

The result of Flags::A ^ Flags::B is 0b0000_0010, which doesn’t correspond to either Flags::A or Flags::B even though it’s still a known bit.

Modules§

example_generated
Example of the generated code by the bitflag macro.
iter
Yield the bits of a source flags value in a set of contained flags values.
parser
Parsing flags from text.

Macros§

bitflag_match
A macro that matches flags values, similar to Rust’s match statement.

Traits§

BitsPrimitive
Primitive types that can be used with bitflag attribute implement this trait.
Flags
A set of defined flags using a bits type as storage.

Attribute Macros§

bitflag
An attribute macro that transforms an C-like enum into a bitflag struct type implementing an API similar to the bitflags crate, and implementing many helpful traits (listed in more details below).