Expand description
Bitflag-attr is a library for Rust that allows to generate types for C-style bitflags with ergonomic APIs using attribute macros and enums.
§Overview
The primary item of this crate is the bitflag
macro. This attribute macro allows to define a
flags type from a Rust C-style enum definition, turning it into a struct with the flags as
associated constants.
The remainder of this documentation is organized as follows:
- Features gives a very brief summary of the features
bitflag-attr
does and does not support. - Usage shows how to add
bitflag-attr
to your Rust project. - Formatting and parsing documents the human-readable format used to parse form and format into strings
- Specification and Terminology documents about the specification and the terminology used by this crate.
- Crate features documents the Cargo features that can be enabled or disabled for this crate.
Also, these sub-modules serve to provide longer form documentation:
§Features
Here is a non-exhaustive list of the things that bitflag-attr
supports:
- Ergonomically create a flags type from native C-like enum syntax
no_std
support with opt-in options to usealloc
andstd
- Generate ergonomic API for the generated flags type
- Generated methods are almost entirely const-compatible
- Generated flags type auto-implements several convenient traits (complete list in the
bitflag
documentation) - Generated
fmt::Debug
implementation outputs human-readable, binary, octal and hexadecimal representation of the flags value for better debugging inspection. - Support to the enum syntax for deriving
Default
by using#[default]
to choose the default flags value - Support to use all integer types and type alias in
core
andstd
as well as most common integer type alias oflibc
with opt-in option to use other type alias as well as custom type alias - Opt-in support for generating
serde::Serialize
andserde::Deserialize
by using theserde
crate feature - Opt-in support for generating
arbitrary::Arbitrary
by using thearbitrary
crate feature - Opt-in support for generating
bytemuck::Zeroable
andbytemuck::Pod
by using thebytemuck
crate feature
§Usage
The bitflag-attr
project is on crates.io and can be
used by adding bitflag-attr
to your dependencies in your project’s Cargo.toml
.
Or more simply, by using cargo add
.
cargo add bitflag-attr
or
[dependencies]
bitflag-attr = "0.11.1"
§Generating flags type
Use the bitflag
attribute macro to generate flags 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 reserved_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
#[reserved_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
bitflag-attr
defines a text format that can be used to convert any flags value to and from
strings.
See the parser
module for more details.
§Specification and Terminology
The terminology and behavior of generated flags types is specified in the documentation module
spec
. Details are repeated in these docs where appropriate, but is exhaustively listed in
the spec. Some things are worth calling out explicitly here.
§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.
§Crate Features
§Ecosystem features
- std — When enabled,
bitflag-attr
will depend on thestd
crate. Currently no particular usage and, for all intents and purposes, the same as thealloc
feature. - alloc — When enabled,
bitflag-attr
will depend on thealloc
crate. In particular, this enables functionality that requires or greatly benefits from dynamic memory allocation. If you can enable this, it is strongly encouraged that you do so. Without it, parsing errors will contain less information for error reporting.
§Code generation features
- serde — When enabled, the
bitflag
macro will handle specially theSerialize
andDeserialize
traits passed to the enum’s derive list and generate a custom implementation of those traits taking into account the human-readable format for the generated flags type. Without it, if these traits are listed in the derive list, they are passed forward to the generated code, i.e. the default derive will be used. This feature does not add theserde
crate to your dependency tree, so your project must have it as a direct dependency. - arbitrary — When enabled, the
bitflag
macro will handle specially theArbitrary
and trait passed to the enum’s derive list and generate a custom implementation of this trait taking into account the known and unknown bits, erroring out on the later. Without it, if this trait are listed in the derive list, they are passed forward to the generated code, i.e. the default derive will be used. This feature does not add thearbitrary
crate to your dependency tree, so your project must have it as a direct dependency. - bytemuck — When enabled, the
bitflag
macro will handle specially theZeroable
andPod
traits passed to the enum’s derive list and generate a custom implementation of those traits with static checks to ensure the correct marking. Without it, if these traits are listed in the derive list, they are passed forward to the generated code, i.e. the default derive will be used. This feature does not add thebytemuck
crate to your dependency tree, so your project must have it as a direct dependency. - custom-types — When enabled, the
bitflag
macro will allow the usage of types as parameters that are not in the list of allowed types at the cost of worse error messages if the used type does not satisfy the requirements for the usage. - const-mut-ref — When enabled, the
bitflag
macro will generate asconst-fn
all generated methods that takes the flags value as mutable pointer. This is only allowed after Rust 1.83.0, and enabling this feature with versions inferior to this number will cause compilation errors. But if you satisfy this version limitation, it is strongly encouraged that you do enable it.
Modules§
- changelog
- A documentation module for this crate changelog.
- example_
generated - A documentation module with a 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.
- spec
- A documentation module for in depth specification definition and terminology.
Macros§
- bitflag_
match - A macro that matches flags values, similar to Rust’s
match
statement.
Traits§
- Bits
Primitive - 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
ergonomic end-user API, similar to the
bitflags
crate, and implementing many helpful traits (listed in more details below).