6 releases (breaking)
Uses old Rust 2015
0.5.0 | Mar 18, 2019 |
---|---|
0.4.0 | May 7, 2017 |
0.3.0 | Jan 31, 2017 |
0.2.0 | Jan 22, 2017 |
0.1.1 | Jan 21, 2017 |
#1195 in Rust patterns
7,765 downloads per month
Used in 81 crates
(7 directly)
31KB
642 lines
bitmask
A bitmask generator for enum scoped bit flags
Usage
Add bitmask
as a dependency in your Cargo.toml
:
[dependency]
bitmask = "^0.5.0"
Then add this snippet to your crate's root:
#[macro_use]
extern crate bitmask;
Features
Bitmask supports one feature: std
. This is enabled by default, and will draw
in the standard library and also automatically derive of Hash
and Debug
for
generated types. If you prefer not to derive these features, then ensure you
do not enable the default features.
Examples
Run a specific example with cargo run --example <name>
.
Similar crates
lib.rs
:
A bitmask generator for enum scoped bit flags.
The bitmask!
macro creates a struct and an enum that holds your flags. The enum contains all the
bit flag variants and the struct is a mixture of those bit flags called a bitmask.
It's syntax is as follows:
bitmask! {
pub mask <struct_name>: <struct_type> where flags <enum_name> {
<flag_name> = <value>,
...
}
}
where pub
is optional and struct_type
can be one of the primitive integer types
(i8-64
, u8-64
, isize
, usize
).
Application
Sometimes you might want to wrap some lib that ports C
or some other code through FFI
which exposes numerous defines/constants as const
. Lets take a look at this example module:
mod tex {
...
pub const TEXTURE_2D: u32 = 1;
pub const TEXTURE_3D: u32 = 2;
pub const FLIP: u32 = 4;
...
pub fn set_options(mask: u32) { ... }
}
To avoid collisions you would use these through the mod scope like so:
tex::set_options(tex::TEXTURE_2D | tex::FLIP);
But that does not guarantee you that you won't use invalid flag values. For example you could do:
set_options(3 | 8);
Now imagine you had an enum to hold all of those flags and a common type that does not accept
any types other than the enum variants and itself. This is exactly what bitmask!
does for you!
It generates an enum with the variants (flags) you supply and a struct that
holds a mask which is a mixture of these variants. So now our example would look like this:
bitmask! {
pub mask TexMask: u32 where flags TexOption {
Texture2d = tex::TEXTURE_2D,
Texture3d = tex::TEXTURE_3D,
Flip = tex::FLIP
}
}
fn set_options(mask: TexMask) {
tex::set_options(*mask);
}
// Single flag
set_options(TexOption::Texture2d.into());
set_options(TexMask::from(TexOption::Texture3d));
// Multiple flags
set_options(TexOption::Texture2d | TexOption::Flip);
Things that are doable but can change with time:
If for some reason you want to define the enum and the struct yourself you can do so and use the
@IMPL
branch of the macro to implement the methods. The only restrictions are that your
struct's inner field must be named mask
and the enum should have the same size as the struct
which can be achieved through the #[repr()]
modifier with the same integer type as the field mask
.
Implementing Into<struct_name>
and Deref
for your own custom type is possible if you want to
use it with the preimplemented methods for the mask but does not apply for the trait implements
like BitOr
for example.
Examples:
bitmask! {
mask BitMask: u32 where flags Flags {
Flag1 = 0x00000001,
Flag2 = 0x000000F0,
Flag3 = 0x00000800,
Flag123 = 0x000008F1,
// Note that function calls like `isize::min_value()`
// can't be used for enum discriminants in Rust.
FlagMax = ::std::u32::MAX
}
}
let mut mask = BitMask::none();
mask.set(Flags::Flag1 | Flags::Flag2);
assert_eq!(*mask, 0x000000F1);
mask.unset(Flags::Flag1);
assert_eq!(*mask, 0x000000F0);
mask.set(Flags::Flag123);
assert_eq!(*mask, 0x000008F1);
You can add meta attributes like documentation (#[doc = "..."]
) to each element of the macro:
bitmask! {
/// Doc comment for the struct
pub mask SomeOtherMask: isize where
/// Doc comment for the enum
flags SomeOtherFlags {
/// Doc comment for the flag
FlagZero = 0,
FlagOne = 1
}
}
Maybe not the best example but still... Cake is love!
bitmask! {
mask Cake: u8 where flags Ingredients {
Sugar = 0b00000001,
Eggs = 0b00000010,
Flour = 0b00000100,
Milk = 0b00001000
}
}
let quality_cake = Cake::all();
assert_eq!(*quality_cake, 0b00001111);