| [](https://github.com/enarx/flagset/actions) |
|  |
| [](https://crates.io/crates/flagset) |
| [](https://docs.rs/flagset) |
|  |
| |
| # Welcome to FlagSet! |
| |
| FlagSet is a new, ergonomic approach to handling flags that combines the |
| best of existing crates like `bitflags` and `enumflags` without their |
| downsides. |
| |
| ## Existing Implementations |
| |
| The `bitflags` crate has long been part of the Rust ecosystem. |
| Unfortunately, it doesn't feel like natural Rust. The `bitflags` crate |
| uses a wierd struct format to define flags. Flags themselves are just |
| integers constants, so there is little type-safety involved. But it doesn't |
| have any dependencies. It also allows you to define implied flags (otherwise |
| known as overlapping flags). |
| |
| The `enumflags` crate tried to improve on `bitflags` by using enumerations |
| to define flags. This was a big improvement to the natural feel of the code. |
| Unfortunately, there are some design flaws. To generate the flags, |
| procedural macros were used. This implied two separate crates plus |
| additional dependencies. Further, `enumflags` specifies the size of the |
| flags using a `repr($size)` attribute. Unfortunately, this attribute |
| cannot resolve type aliases, such as `c_int`. This makes `enumflags` a |
| poor fit for FFI, which is the most important place for a flags library. |
| The `enumflags` crate also disallows overlapping flags and is not |
| maintained. |
| |
| FlagSet improves on both of these by adopting the `enumflags` natural feel |
| and the `bitflags` mode of flag generation; as well as additional API usage |
| niceties. FlagSet has no dependencies and is extensively documented and |
| tested. It also tries very hard to prevent you from making mistakes by |
| avoiding external usage of the integer types. FlagSet is also a zero-cost |
| abstraction: all functions are inlineable and should reduce to the core |
| integer operations. FlagSet also does not depend on stdlib, so it can be |
| used in `no_std` libraries and applications. |
| |
| ## Defining Flags |
| |
| Flags are defined using the `flags!` macro: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| use std::os::raw::c_int; |
| |
| flags! { |
| enum FlagsA: u8 { |
| Foo, |
| Bar, |
| Baz, |
| } |
| |
| enum FlagsB: c_int { |
| Foo, |
| Bar, |
| Baz, |
| } |
| } |
| ``` |
| |
| Notice that a flag definition looks just like a regular enumeration, with |
| the addition of the field-size type. The field-size type is required and |
| can be either a type or a type alias. Both examples are given above. |
| |
| Also note that the field-size type specifies the size of the corresponding |
| `FlagSet` type, not size of the enumeration itself. To specify the size of |
| the enumeration, use the `repr($size)` attribute as specified below. |
| |
| ## Flag Values |
| |
| Flags often need values assigned to them. This can be done implicitly, |
| where the value depends on the order of the flags: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| |
| flags! { |
| enum Flags: u16 { |
| Foo, // Implicit Value: 0b0001 |
| Bar, // Implicit Value: 0b0010 |
| Baz, // Implicit Value: 0b0100 |
| } |
| } |
| ``` |
| |
| Alternatively, flag values can be defined explicitly, by specifying any |
| `const` expression: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| |
| flags! { |
| enum Flags: u16 { |
| Foo = 0x01, // Explicit Value: 0b0001 |
| Bar = 2, // Explicit Value: 0b0010 |
| Baz = 0b0100, // Explicit Value: 0b0100 |
| } |
| } |
| ``` |
| |
| Flags can also overlap or "imply" other flags: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| |
| flags! { |
| enum Flags: u16 { |
| Foo = 0b0001, |
| Bar = 0b0010, |
| Baz = 0b0110, // Implies Bar |
| All = (Flags::Foo | Flags::Bar | Flags::Baz).bits(), |
| } |
| } |
| ``` |
| |
| ## Specifying Attributes |
| |
| Attributes can be used on the enumeration itself or any of the values: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| |
| flags! { |
| #[derive(PartialOrd, Ord)] |
| enum Flags: u8 { |
| Foo, |
| #[deprecated] |
| Bar, |
| Baz, |
| } |
| } |
| ``` |
| |
| ## Collections of Flags |
| |
| A collection of flags is a `FlagSet<T>`. If you are storing the flags in |
| memory, the raw `FlagSet<T>` type should be used. However, if you want to |
| receive flags as an input to a function, you should use |
| `impl Into<FlagSet<T>>`. This allows for very ergonomic APIs: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| |
| flags! { |
| enum Flags: u8 { |
| Foo, |
| Bar, |
| Baz, |
| } |
| } |
| |
| struct Container(FlagSet<Flags>); |
| |
| impl Container { |
| fn new(flags: impl Into<FlagSet<Flags>>) -> Container { |
| Container(flags.into()) |
| } |
| } |
| |
| assert_eq!(Container::new(Flags::Foo | Flags::Bar).0.bits(), 0b011); |
| assert_eq!(Container::new(Flags::Foo).0.bits(), 0b001); |
| assert_eq!(Container::new(None).0.bits(), 0b000); |
| ``` |
| |
| ## Operations |
| |
| Operations can be performed on a `FlagSet<F>` or on individual flags: |
| |
| | Operator | Assignment Operator | Meaning | |
| |----------|---------------------|------------------------| |
| | \| | \|= | Union | |
| | & | &= | Intersection | |
| | ^ | ^= | Toggle specified flags | |
| | - | -= | Difference | |
| | % | %= | Symmetric difference | |
| | ! | | Toggle all flags | |
| |
| ## Optional Serde support |
| |
| [Serde] support can be enabled with the 'serde' feature flag. You can then serialize and |
| deserialize `FlagSet<T>` to and from any of the [supported formats]: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| |
| flags! { |
| enum Flags: u8 { |
| Foo, |
| Bar, |
| } |
| } |
| |
| let flagset = Flags::Foo | Flags::Bar; |
| let json = serde_json::to_string(&flagset).unwrap(); |
| let flagset: FlagSet<Flags> = serde_json::from_str(&json).unwrap(); |
| assert_eq!(flagset.bits(), 0b011); |
| ``` |
| |
| For serialization and deserialization of flags enum itself, you can use the [`serde_repr`] crate |
| (or implement `serde::ser::Serialize` and `serde:de::Deserialize` manually), combined with the |
| appropriate `repr` attribute: |
| |
| ```rust |
| use flagset::{FlagSet, flags}; |
| use serde_repr::{Serialize_repr, Deserialize_repr}; |
| |
| flags! { |
| #[repr(u8)] |
| #[derive(Deserialize_repr, Serialize_repr)] |
| enum Flags: u8 { |
| Foo, |
| Bar, |
| } |
| } |
| |
| let json = serde_json::to_string(&Flags::Foo).unwrap(); |
| let flag: Flags = serde_json::from_str(&json).unwrap(); |
| assert_eq!(flag, Flags::Foo); |
| ``` |
| |
| [Serde]: https://serde.rs/ |
| [supported formats]: https://serde.rs/#data-formats |
| [`serde_repr`]: https://crates.io/crates/serde_repr |