Add core::mem::offset_of!
RFC by thomcc · Pull Request #3308 · rust-lang/rfcs (original) (raw)
@afetisov (your comments are long enough to get a separate reply)
I think the RFC should have some motivation for allowing offset_of! on non-
#[repr(C)]
types. "memoffset
does it" isn't sufficient justification, sincememoffset
has no way to enforce the layout requirements on the supplied types.
The use cases for this are largely the same as the use cases for using offset_of!
on repr(C) types -- {de,}serialization, debugging support code, FFI, and data structures -- in all of these it may be undesirable or impractical to force all types to use repr(C).
More generally, it is a goal of this RFC to remove the reasons people have to implement offset_of!
manually (either explicitly, or often in an ad-hoc manner). If core::mem::offset_of!
lacks functionality that you could implement yourself, then this adds more motivation to not use it.
One important thing is missing from the reference: the layout of #[repr(rust)] data is unspecified, but is it even true that the layout is fixed?
In several ways it is already fixed at compile time (size and alignment are, which limits the size of the overall structure). In other ways, it is fixed at runtime. Allowing use in const evaluation prevents an implementation from doing things like randomizing field offsets at startup, unless it also performs modifications to adjust offset_of!
values (and calculations derived from them).
My suspicion is that many implementations which can randomize field offsets at startup can recompute constant values (because they're likely interpreted), but it's plausible that there's some middle-ground.
Either way, this is more or less what I'm getting at by the first item under "Drawbacks" -- previously this information was only available at runtime, and this allows accessing it at compile time.
I can totally imagine a compiler optimization that e.g. uses different layout for different local variables of the same type, provided they do not "escape" in some sense (e.g. a type T is private and no explicit pointers to T are created)
This kind of optimization (and more aggressive ones, like SROA) would still be allowed in exactly the same cases they are currently allowed. If a pointer to an instance of a structure does not escape, the offsets of its field do not matter. This is true regardless of the #[repr]
of the type, and is not hindered or changed by offset_of!
at all, as that information can already be computed using address arithmetic on pointers to the structure and field.
Being a macro,
offset_of!
also can't distinguish between structs and enums, or between fields and arbitrary garbage. I'm afraid this will lead to some very poor post-monomorphization errors
Are you sure? From my experience with the code in rustc_builtin_macros
and from conversations with compiler contributors (which I had when preparing this RFC), none of this should be a problem.
Perhaps someone familiar with compiler internals cares to weigh in?
One possibility which isn't considered in the RFC is to use special constants instead of an expression macro
I'll add it to the RFC as an alternative, but I am not a fan of macros that expand to items not present in the source (you'll note that none of the existing builtin #[derive()]
s do this); I find it results in poor tooling experience, and is generally confusing. Additionally:
I don't think there's any reason a derive macro would have more information here than is available to a builtin macro. They're essentially the same thing, and have essentially the same limitations.
This implementation is also very hard to provide in a library crate, which provides a stronger argument for including it into the core.
I don't think this is true -- if core::mem::offset_of!
is added, I think it would be quite straightforward to implement a #[derive(FieldOffsets)]
equivalent to what you describe as a procedural macro library... What would be difficult about it?
I'll try to update drawbacks/alternatives/motivation with this feedback.