Open nerditation opened 11 months ago
@fu5ha this seems like something for you since you're the proc-macro guardian
👍 agree with the analysis, feel free to PR and I will review or ping me in a couple weeks and I can hopefully implement it
I tried to take a look at the code. I find it more complicated then expected. the main issue is, derive
macros don't receive the derive
attribute itself. for example, given the definition:
#[derive(Foo, Bar)]
struct MyStruct {}
the procedural macro of Foo
cannot see Bar
is present; same for Bar
.
in this case, CheckedBitPattern
cannot determine whether the type derives NoUninit
or not. I can think of two possible solutions here:
let the user annotate with a helper attribute for the CheckedBitPattern
derive macro, something like:
#[derive(Clone, Copy, NoUninit, CheckedBitPattern)]
#[checked_bits_type(additional_traits(NoUninit))]
struct MyStruct {}
Bits
type, not limited to NoUninit
;NoUninit
need to be repeateduse the unstable #![allow_trivial_constraints]
feature and put the implementation behind a feature gate.
the implementation is just as simple as:
unsafe impl NoUninit for FooBits where Foo: NoUninit {}
#derive
but manually unsafe impl NoUninit
.there's also another "non-solution": in document, ask the user to write multiple #[derive()]
separately, and in specific order. for example, from my testing, in the following code:
#[derive(Foo)]
#[derive(Bar)]
struct MyStruct {}
the input token stream of Foo
contains the attribute #[derive(Bar)]
, but not the other way around. so if we write #[derive(NoUninit)]
after #[derive(CheckedBitPattern)]
, the derive macro of CheckedBitPattern
will see the NoUninit
derive attribute.
but this is a "non-solution", not only because it is delicate and feels "hacky", but most importantly, rustfmt
will merge separate #[derive()]
attribtes into one single attribute, so it doesn't actually work at all.
at this point, I feel like it's probably just not worth it to add this feature.
anyway, I'm not an expert on procedural macros, maybe I'm missing something obvious. @fu5ha any opinions or suggestions? thanks.
summary
currently, the
#[derive(CheckedBitPattern)]
on a structFoo
expands to code like this:however, the generated
FooBits
type cannot be used as a buffer which will be later read into, becausebytes_of_mut
requires the type to beNoUninit + AnyBitPattern
, whileFooBits
doesn't implementNoUninit
, even ifFoo
isNoUninit
.I suggest the derive macro for
CheckedBitPattern
to add an implmentation ofNoUninit
for the generated "Bits" type, something like:context
I was using
CheckedBitPattern
as a validator for very simple binary file format. I could write something like this:but I would like to use the
HeaderBits
as buffer, like this:but the code won't compile, unless I manually add:
my
Header
type looks like this:btw, it would also be convenient if the
CheckedBitPattern
trait could have a conversion function, e.g.try_from_bits()
, something like:or, alternatively, we can add an
try_into_checked()
method for the generated "Bits" type.