That's... a good question I overlooked. I was originally thinking it would have the full definition of type
available (and so could evaluate to a usize
literal), but I realize now that macro expansion happens way too early for the macro evaluator to have the full definition of type
. Here are a couple alternatives (neither of which are ideal, and neither of which I expect to be the final accepted solution; I'm just trying to get the idea-ball rolling):
Ignoring Deref
If we can ignore Deref
for now, this could be implemented with RFC 2582 like so:
macro_rules! offset_of {
($ty:ty, $field:ident $(,)?) => ({
let null = 0usize as *const $ty;
$crate::mem::transmute::<_, usize>(&(*null).$field as *const _)
});
}
This should be const-eval friendly too, even on current rust. It also is compatible with sub-fields (e.g., offset_of!(Struct, inner.inner_field)
). The only downsides I can see are:
- It doesn't avoid going through
Deref
. AvoidingDeref
might require new special syntax. - It doesn't work if
&field
results in a fat pointer. That could be fixed by doing proper pointer subtraction instead of transmuting (I know someone's going to remind me that "pointers aren't just integers", which I'm well aware of). I should edit the post to fix that but it's time to do my $dayjob.
Crazy per-type traits
If a special trait was automatically defined by the compiler for each type (where each type gets its own trait), then it could be:
macro_rules! offset_of {
($ty:ty, $field:ident $(,)?) => ({
<$ty as CrazyBuiltInTraitCustomMadeFor<$ty>>::$field
});
}
That is, the trait CrazyBuiltInTraitCustomMadeFor
is automatically defined by the compiler for each type, and it contains associated consts that share the name of the struct's fields, where each const is the offset of the field within the type. I haven't thought this through much, so there might be some major complications I'm overlooking (e.g., I'm not sure how this would work with sub-fields, nor how it should work with tuples which have integers for field names).
It could also be an actual type that has a custom impl
for each type (so instead of doing <$ty as CrazyBuiltInTraitCustomMadeFor<$ty>>::$field
in the macro, it would be CrazyBuiltInType<$ty>::$field
).
Anyway, I'll have to give the expansion of the macro more thought. Thanks for bringing that up.