Sending an answer that uses a macro as one of the possible solutions to the question.
Usage example:
struct_bitflag_impl!(pub struct MyFlag(pub u8)); pub struct MyFlag(u8); struct_bitflag_impl!(MyFlag); pub struct MyOtherFlag(u32); struct_bitflag_impl!(MyOtherFlag);
- Type safe.
- Zero-level overhead compared to regular integer types.
- The base value is available from
value.0 , if necessary. - Uses one macro:
struct_bitflag_impl , which can be reused and applied to several types of structures.
Each ad has only 2 lines.
Macro:
/// Implements bitflag operators for integer struct, eg: /// ``` /// pub struct MyFlag(u8); /// struct_bitflag_impl!(MyFlag); /// ``` macro_rules! struct_bitflag_impl { ($p:ident) => { // Possible additions: // * left/right shift. // * Deref to forward methods to the underlying type. impl ::std::ops::BitAnd for $p { type Output = $p; fn bitand(self, _rhs: $p) -> $p { $p(self.0 & _rhs.0) } } impl ::std::ops::BitOr for $p { type Output = $p; fn bitor(self, _rhs: $p) -> $p { $p(self.0 | _rhs.0) } } impl ::std::ops::BitXor for $p { type Output = $p; fn bitxor(self, _rhs: $p) -> $p { $p(self.0 ^ _rhs.0) } } impl ::std::ops::Not for $p { type Output = $p; fn not(self) -> $p { $p(!self.0) } } impl ::std::ops::BitAndAssign for $p { fn bitand_assign(&mut self, _rhs: $p) { self.0 &= _rhs.0; } } impl ::std::ops::BitOrAssign for $p { fn bitor_assign(&mut self, _rhs: $p) { self.0 |= _rhs.0; } } impl ::std::ops::BitXorAssign for $p { fn bitxor_assign(&mut self, _rhs: $p) { self.0 ^= _rhs.0; } } // Other operations needed to be generally usable. impl PartialEq for $p { fn eq(&self, other: &$p) -> bool { self.0 == other.0 } } impl Copy for $p { } impl Clone for $p { fn clone(&self) -> $p { $p(self.0) } } } }
For an alternate version of this macro that derive supports, which is needed, so constants of this type can be used in the match statement.
It also avoids the need to copy and clone.
struct_bitflag_impl!(pub struct MyFlag(pub u8));
Macro:
macro_rules! struct_bitflag_impl { // pub/pub (pub struct $name:ident ( pub $t:tt ) ) => { #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub struct $name(pub $t); _struct_bitflag_gen_impls!($name, $t); }; // private/pub (struct $name:ident ( pub $t:tt ) ) => { #[derive(PartialEq, Eq, Copy, Clone, Debug)] struct $name(pub $t); _struct_bitflag_gen_impls!($name, $t); }; // pub/private (pub struct $name:ident ( $t:tt ) ) => { #[derive(PartialEq, Eq, Copy, Clone, Debug)] struct $name($t); _struct_bitflag_gen_impls!($name, $t); }; // private/private (struct $name:ident ( $t:tt ) ) => { #[derive(PartialEq, Eq, Copy, Clone, Debug)] struct $name($t); _struct_bitflag_gen_impls!($name, $t); } } macro_rules! _struct_bitflag_gen_impls { ($t:ident, $t_base:ident) => { impl ::std::ops::BitAnd for $t { type Output = $t; #[inline] fn bitand(self, _rhs: $t) -> $t { $t(self.0 & _rhs.0) } } impl ::std::ops::BitOr for $t { type Output = $t; #[inline] fn bitor(self, _rhs: $t) -> $t { $t(self.0 | _rhs.0) } } impl ::std::ops::BitXor for $t { type Output = $t; #[inline] fn bitxor(self, _rhs: $t) -> $t { $t(self.0 ^ _rhs.0) } } impl ::std::ops::Not for $t { type Output = $t; #[inline] fn not(self) -> $t { $t(!self.0) } } impl ::std::ops::BitAndAssign for $t { #[inline] fn bitand_assign(&mut self, _rhs: $t) { self.0 &= _rhs.0; } } impl ::std::ops::BitOrAssign for $t { #[inline] fn bitor_assign(&mut self, _rhs: $t) { self.0 |= _rhs.0; } } impl ::std::ops::BitXorAssign for $t { #[inline] fn bitxor_assign(&mut self, _rhs: $t) { self.0 ^= _rhs.0; } } /// Support for comparing with the base type, allows comparison with 0. /// /// This is used in typical expressions, eg: `if (a & FLAG) != 0 { ... }` /// Having to use MyFlag(0) all over is too inconvenient. impl PartialEq<$t_base> for $t {