phantom_fields!() { /* proc-macro */ }
Expand description
Declares a struct to have “phantom” fields. Use within an impl
block.
At the moment, this only supports tuple structs and only targets the 0
field of said structs. It’s intended for use with newtype’d unsigned
integers of various sizes.
The proc-macro’s grammar is fairly simple once you get the hang of it but there’s several possibilities you can declare.
self.0: [TYPE],
to declare the type of theself.0
field.- Then you declare 0 or more phantom fields:
- Each phantom field can have attributes, which is mostly intended for allowing rustdoc.
- Then the name of the field, followed by
:
- Then you describe “where” the field is with a
,
at the end:- A single integer makes a bool field at that bit.
- An
a-b
integer span makes an integer field in that inclusive bit range. - An
a-b=ENUM<TAG1,...,TAG_N>
location makes an enum field of the type given that can be any of the tags you specify. In the decoding method an_ => unreachable!()
branch is placed after all tags, so if you specify less tags than possible bit patterns (eg: only 3 tags for a 2-bit field) your code can potentially panic. - An identifier will assume that the identifier is already a const declared somewhere in scope, and use that as the bit location of a bool-typed field.
- Enum and integer phantom fields also get a
FOO_MASK
const added to the struct type for each field namedfoo
. This const is what takes on the attributes you specified for that phantom field. - Bool phantom fields get a
FOO_BIT
const added to the struct for each field named foo. This gets the attributes of the field. - Const phantom fields don’t get added to the struct, they’re already a const somewhere after all.
Once this is all set up you’ll of course want to use the phantom fields:
- phantom fields can be read using their name as a the method name, similar to normal rust “getters”.
- phantom fields can be replaced “builder style” by taking
self
and giving a new value usingwith_field
. This isconst
, so you can use it to declare a const that has some particular setting combination.
I think it makes more sense if you see it in action. Here’s an extended example of all the situations supported.
use phantom_fields::phantom_fields;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum DisplayMode {
Good = 0,
Bad = 1,
Ugly = 2,
}
pub const CONST_TEST_VALUE: u32 = 1 << 13;
#[derive(Debug, Default, Clone, Copy)]
#[repr(transparent)]
pub struct PhantomFieldsDemo(u32);
impl PhantomFieldsDemo {
phantom_fields! {
self.0: u32,
/// enum_example docs
enum_example: 0-2=DisplayMode<Good, Bad, Ugly>,
bool_example: 3,
/// This gives us a 2-bit field
int_example: 6-7,
const_example: CONST_TEST_VALUE,
}
}