Bitmask Types
Bitmasks are mapped to newtype structs in Rust, where each value is an
associated constant of the newtype. This effectively scopes the value of each
constant into the bitmask type, and prevents doing bitwise operations with
different types. The underlying integer can be accessed through the bits
function.
The implementation uses a bitmask! macro to generate the type and all
required trait implementations. The generated API is equivalent to the expanded
form shown below.
Bitmasks are always trivial (they wrap a primitive integer) and have total
order, so they derive Copy, Debug, Eq, Ord, and Hash. See
Derived Traits for the general trait derivation
rules.
The #[repr(transparent)] attribute guarantees that the newtype has the same
memory layout as the underlying integer type. This ensures ABI compatibility
when passing bitmasks across FFI boundaries and allows safe transmutation
between the bitmask and its underlying type.
// IDL
@bit_bound(32)
bitmask MyBitmask {
A,
B,
@position(5) C
};
#![allow(unused)]
fn main() {
// Rust
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct MyBitmask(u32);
impl MyBitmask {
pub const A: Self = Self(1 << 0);
pub const B: Self = Self(1 << 1);
pub const C: Self = Self(1 << 5);
#[inline]
pub const fn nil() -> Self {
Self(0)
}
#[inline]
pub const fn all() -> Self {
Self(Self::A.0 | Self::B.0 | Self::C.0)
}
#[inline]
pub const fn bits(&self) -> u32 {
self.0
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
#[inline]
pub const fn contains(&self, rhs: Self) -> bool {
(self.0 & rhs.0) == rhs.0
}
#[inline]
pub fn clear(&mut self) {
self.0 = 0
}
}
impl ::std::ops::BitOr for MyBitmask {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl ::std::ops::BitOrAssign for MyBitmask {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl ::std::ops::BitXor for MyBitmask {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
Self(self.0 ^ rhs.0)
}
}
impl ::std::ops::BitXorAssign for MyBitmask {
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0
}
}
impl ::std::ops::BitAnd for MyBitmask {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl ::std::ops::BitAndAssign for MyBitmask {
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0
}
}
impl ::std::ops::Not for MyBitmask {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
Self(!self.0)
}
}
}