1[](https://github.com/enarx/flagset/actions) 2 3[](https://crates.io/crates/flagset) 4[](https://docs.rs/flagset) 5 6 7# Welcome to FlagSet! 8 9FlagSet is a new, ergonomic approach to handling flags that combines the 10best of existing crates like `bitflags` and `enumflags` without their 11downsides. 12 13## Existing Implementations 14 15The `bitflags` crate has long been part of the Rust ecosystem. 16Unfortunately, it doesn't feel like natural Rust. The `bitflags` crate 17uses a wierd struct format to define flags. Flags themselves are just 18integers constants, so there is little type-safety involved. But it doesn't 19have any dependencies. It also allows you to define implied flags (otherwise 20known as overlapping flags). 21 22The `enumflags` crate tried to improve on `bitflags` by using enumerations 23to define flags. This was a big improvement to the natural feel of the code. 24Unfortunately, there are some design flaws. To generate the flags, 25procedural macros were used. This implied two separate crates plus 26additional dependencies. Further, `enumflags` specifies the size of the 27flags using a `repr($size)` attribute. Unfortunately, this attribute 28cannot resolve type aliases, such as `c_int`. This makes `enumflags` a 29poor fit for FFI, which is the most important place for a flags library. 30The `enumflags` crate also disallows overlapping flags and is not 31maintained. 32 33FlagSet improves on both of these by adopting the `enumflags` natural feel 34and the `bitflags` mode of flag generation; as well as additional API usage 35niceties. FlagSet has no dependencies and is extensively documented and 36tested. It also tries very hard to prevent you from making mistakes by 37avoiding external usage of the integer types. FlagSet is also a zero-cost 38abstraction: all functions are inlineable and should reduce to the core 39integer operations. FlagSet also does not depend on stdlib, so it can be 40used in `no_std` libraries and applications. 41 42## Defining Flags 43 44Flags are defined using the `flags!` macro: 45 46```rust 47use flagset::{FlagSet, flags}; 48use std::os::raw::c_int; 49 50flags! { 51 enum FlagsA: u8 { 52 Foo, 53 Bar, 54 Baz, 55 } 56 57 enum FlagsB: c_int { 58 Foo, 59 Bar, 60 Baz, 61 } 62} 63``` 64 65Notice that a flag definition looks just like a regular enumeration, with 66the addition of the field-size type. The field-size type is required and 67can be either a type or a type alias. Both examples are given above. 68 69Also note that the field-size type specifies the size of the corresponding 70`FlagSet` type, not size of the enumeration itself. To specify the size of 71the enumeration, use the `repr($size)` attribute as specified below. 72 73## Flag Values 74 75Flags often need values assigned to them. This can be done implicitly, 76where the value depends on the order of the flags: 77 78```rust 79use flagset::{FlagSet, flags}; 80 81flags! { 82 enum Flags: u16 { 83 Foo, // Implicit Value: 0b0001 84 Bar, // Implicit Value: 0b0010 85 Baz, // Implicit Value: 0b0100 86 } 87} 88``` 89 90Alternatively, flag values can be defined explicitly, by specifying any 91`const` expression: 92 93```rust 94use flagset::{FlagSet, flags}; 95 96flags! { 97 enum Flags: u16 { 98 Foo = 0x01, // Explicit Value: 0b0001 99 Bar = 2, // Explicit Value: 0b0010 100 Baz = 0b0100, // Explicit Value: 0b0100 101 } 102} 103``` 104 105Flags can also overlap or "imply" other flags: 106 107```rust 108use flagset::{FlagSet, flags}; 109 110flags! { 111 enum Flags: u16 { 112 Foo = 0b0001, 113 Bar = 0b0010, 114 Baz = 0b0110, // Implies Bar 115 All = (Flags::Foo | Flags::Bar | Flags::Baz).bits(), 116 } 117} 118``` 119 120## Specifying Attributes 121 122Attributes can be used on the enumeration itself or any of the values: 123 124```rust 125use flagset::{FlagSet, flags}; 126 127flags! { 128 #[derive(PartialOrd, Ord)] 129 enum Flags: u8 { 130 Foo, 131 #[deprecated] 132 Bar, 133 Baz, 134 } 135} 136``` 137 138## Collections of Flags 139 140A collection of flags is a `FlagSet<T>`. If you are storing the flags in 141memory, the raw `FlagSet<T>` type should be used. However, if you want to 142receive flags as an input to a function, you should use 143`impl Into<FlagSet<T>>`. This allows for very ergonomic APIs: 144 145```rust 146use flagset::{FlagSet, flags}; 147 148flags! { 149 enum Flags: u8 { 150 Foo, 151 Bar, 152 Baz, 153 } 154} 155 156struct Container(FlagSet<Flags>); 157 158impl Container { 159 fn new(flags: impl Into<FlagSet<Flags>>) -> Container { 160 Container(flags.into()) 161 } 162} 163 164assert_eq!(Container::new(Flags::Foo | Flags::Bar).0.bits(), 0b011); 165assert_eq!(Container::new(Flags::Foo).0.bits(), 0b001); 166assert_eq!(Container::new(None).0.bits(), 0b000); 167``` 168 169## Operations 170 171Operations can be performed on a `FlagSet<F>` or on individual flags: 172 173| Operator | Assignment Operator | Meaning | 174|----------|---------------------|------------------------| 175| \| | \|= | Union | 176| & | &= | Intersection | 177| ^ | ^= | Toggle specified flags | 178| - | -= | Difference | 179| % | %= | Symmetric difference | 180| ! | | Toggle all flags | 181 182## Optional Serde support 183 184[Serde] support can be enabled with the 'serde' feature flag. You can then serialize and 185deserialize `FlagSet<T>` to and from any of the [supported formats]: 186 187 ```rust 188 use flagset::{FlagSet, flags}; 189 190 flags! { 191 enum Flags: u8 { 192 Foo, 193 Bar, 194 } 195 } 196 197 let flagset = Flags::Foo | Flags::Bar; 198 let json = serde_json::to_string(&flagset).unwrap(); 199 let flagset: FlagSet<Flags> = serde_json::from_str(&json).unwrap(); 200 assert_eq!(flagset.bits(), 0b011); 201 ``` 202 203For serialization and deserialization of flags enum itself, you can use the [`serde_repr`] crate 204(or implement `serde::ser::Serialize` and `serde:de::Deserialize` manually), combined with the 205appropriate `repr` attribute: 206 207 ```rust 208 use flagset::{FlagSet, flags}; 209 use serde_repr::{Serialize_repr, Deserialize_repr}; 210 211 flags! { 212 #[repr(u8)] 213 #[derive(Deserialize_repr, Serialize_repr)] 214 enum Flags: u8 { 215 Foo, 216 Bar, 217 } 218 } 219 220 let json = serde_json::to_string(&Flags::Foo).unwrap(); 221 let flag: Flags = serde_json::from_str(&json).unwrap(); 222 assert_eq!(flag, Flags::Foo); 223 ``` 224 225[Serde]: https://serde.rs/ 226[supported formats]: https://serde.rs/#data-formats 227[`serde_repr`]: https://crates.io/crates/serde_repr 228