How to declare typed bitflags in Rust?

后端 未结 4 2126
-上瘾入骨i
-上瘾入骨i 2021-01-26 05:48

It\'s possible to declare flags in Rust - similar to how it would be done in C.

pub const FOO: u32 = (1 << 0);
pub const BAR: u32 = (1 << 1);

let fl         


        
4条回答
  •  日久生厌
    2021-01-26 06:16

    Posting answer which uses a macro as one possible solution to the question.

    Example usage:

    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 overhead compared with plain integer types.
    • Underlying value is accessible from value.0 if needed.
    • Uses a single macro: struct_bitflag_impl which can be re-used and applied to multiple struct types.
      Each declaration is only 2 lines.

    The 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 alternative variation on this macro that supports derive which is needed so constants of this type can be used in a match statement can be written.

    This also avoids having to define Copy & Clone.

    struct_bitflag_impl!(pub struct MyFlag(pub u8));
    

    The 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 {
                #[inline]
                fn eq(&self, other: &$t_base) -> bool { self.0 == *other }
            }
        }
    }
    

提交回复
热议问题