Reputation: 656
I have an enum that looks like this:
#[repr(u8)]
pub enum PublicFlags {
PublicFlagVersion = 0x01,
PublicFlagReset = 0x02,
NoncePresent = 0x04,
IdPresent = 0x08,
PktNumLen4 = 0x30,
PktNumLen2 = 0x20,
PktNumLen1 = 0x10,
Multipath = 0x40,
}
I want to do a bitwise operation on several of the enum values. However, the Rust compiler complains:
an implementation of `std::ops::BitAnd` might be missing for `PublicFlags`.
Upvotes: 27
Views: 24595
Reputation: 2035
As an alternative to bitflags mentioned in another answer you can youse enumflags2. The latter is based on enums.
use enumflags2::{bitflags, BitFlags};
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum Flags {
Bit1 = 1 << 7,
Bit2 = 1 << 6,
Bit3 = 1 << 5,
Bit4 = 1 << 4,
Bit5 = 1 << 3,
Bit6 = 1 << 2,
Bit7 = 1 << 1,
Bit8 = 1,
}
fn main() {
let flags_1_2: BitFlags<Flags> = Flags::Bit1 | Flags::Bit2;
let flags_2_3: BitFlags<Flags> = Flags::Bit2 | Flags::Bit3;
assert!(flags_1_2.contains(Flags::Bit1));
assert!(!flags_1_2.contains(Flags::Bit3));
// Union of two flag sets
assert!((flags_1_2 | flags_2_3).contains(Flags::Bit1 | Flags::Bit2 | Flags::Bit3));
// Intersection of two flag sets
assert_eq!(flags_1_2 & flags_2_3, Flags::Bit2);
// Flip flags
assert!((!flags_1_2).contains(Flags::Bit3 | Flags::Bit4 | Flags::Bit5 /* and so on */));
// You can still use your original enum
let enum_flags = Flags::Bit1;
// Here match guards are unions of enum variants but arm values are `BitFlags`
let bitflags_from_enum = match enum_flags {
Flags::Bit1 | Flags::Bit2 | Flags::Bit3 => Flags::Bit1 | Flags::Bit2 | Flags::Bit3,
Flags::Bit4 | Flags::Bit5 | Flags::Bit6 => Flags::Bit4 | Flags::Bit5 | Flags::Bit6,
Flags::Bit7 | Flags::Bit8 => Flags::Bit7 | Flags::Bit8,
};
assert!(bitflags_from_enum.contains(Flags::Bit1 | Flags::Bit2 | Flags::Bit3));
}
Upvotes: 1
Reputation: 1268
An enum
in Rust is not intended to be used as bit flags. PublicFlags
can only take the values given in the enum (and not a combination). So for instance, the following match statement is exhaustive:
let flags: PublicFlags;
...
match flags {
PublicFlagVersion => {...}
PublicFlagReset => {...}
NoncePresent => {...}
IdPresent => {...}
PktNumLen4 => {...}
PktNumLen2 => {...}
PktNumLen1 => {...}
Multipath => {...}
}
There is no way to have a PublicFlags
variable with a combination of the flags.
The solution is to actually store the value as a u8
, then use constants to store the value of each flag. This can be cumbersome, but thankfully the bitflags crate wraps all the boilerplate up in a macro for you. Here is an example how you would create your struct using bitflags:
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct PublicFlags: u8 {
const PUBLIC_FLAG_VERSION = 0x01;
const PUBLIC_FLAG_RESET = 0x02;
const NONCE_PRESENT = 0x04;
const ID_PRESENT = 0x08;
const PKT_NUM_LEN_4 = 0x30;
const PKT_NUM_LEN_2 = 0x20;
const PKT_NUM_LEN_1 = 0x10;
const MULTIPATH = 0x40;
}
}
fn main() {
let flag = PublicFlags::PUBLIC_FLAG_VERSION | PublicFlags::ID_PRESENT;
assert!((flag & PublicFlags::MULTIPATH).is_empty());
assert!(flag.contains(PublicFlags::ID_PRESENT));
}
Upvotes: 36
Reputation: 3034
I don't know if this existed back then, but a bit of as
magic does the job without any bitor/bitand trait implementation (makes the code a bit longer but it is very simple)
enum BITS {
ONE = 0b0001,
TWO = 0b0010,
FOUR = 0b0100,
EIGHT = 0b1000,
}
fn main() {
let flags: u8;
flags = BITS::ONE as u8 | BITS::FOUR as u8 | BITS::EIGHT as u8;
println!("{flags:b}")
use_flags(flags);
}
fn use_flags(flags:u8){
let flag_one = flags & BITS::ONE as u8 == 1;
if flag_one {
println!("flag ONE is {flag_one}")
}
}
PS: you will get warning of this shape if you don't use all enums in the code: "warning: variant TWO
is never constructed", but, again, it is just a warning that can be handled gracefully.
NOTE: when using enums for bitwise operations, item values should not overlap. In the code of the main question, at least PktNumLen1
will overlap with PublicFlagReset
and IdPresent
, 0x10 is 0b1010, 0x8 is 0b1000, and 0x2 is 0b0010 (2nd and 4th bits overlap)
Upvotes: 1
Reputation: 3034
I have already an answer here on how to setup and use an enum if we want bitwise operations.
On the other hand, we can combine enum values in a vector (dynamic array) and use them as they are:
#[derive(Debug)]
enum BITS {
ONE = 0b0001,
TWO = 0b0010,
FOUR = 0b0100,
EIGHT = 0b1000,
}
fn main() {
let flags: Vec<BITS>;
flags = vec![BITS::ONE, BITS::FOUR, BITS::EIGHT];
println!("{flags:?}");
use_flags(flags);
}
fn use_flags(flags:Vec<BITS>){
for flag in flags{
match flag {
BITS::ONE => println!("flag ONE is set"),
BITS::TWO => println!("flag TWO is set"),
BITS::FOUR => println!("flag FOUR is set"),
BITS::EIGHT => println!("flag EIGHT is set")
}
}
}
the thing here is to set default values (with mut) inside our function and use the match
to customize them later down the code.
it is easy to code but the downside of this approach is the memory usage for each flag inside the vector.
in the bit operation approach, bits of enum values should not overlap. the forethought process is essential in that approach. and also unpacking bit fields is not easy, yet storing them in bit fields makes it efficient for memory. so check my other answer too. (https://stackoverflow.com/a/76603396/9512475)
Upvotes: 0
Reputation: 4580
This could work as an alternative answer without new dependencies.
pub mod PublicFlags {
pub const PublicFlagVersion: u8 = 0x01;
pub const PublicFlagReset: u8 = 0x02;
pub const NoncePresent: u8 = 0x04;
pub const IdPresent: u8 = 0x08;
pub const PktNumLen4: u8 = 0x30;
pub const PktNumLen2: u8 = 0x20;
pub const PktNumLen1: u8 = 0x10;
pub const Multipath: u8 = 0x40;
}
You can refer to the values just like with an enum with PublicFlags::PublicFlagVersion
, or add use PublicFlags::*;
if it is cleaner to reference the values without specifying the namespace.
Upvotes: 14