In some cases you want to get the address of a field of a struct, without there actually being an instance of an allocated object there. A simple example is the vulkano
crates impl_vertex
macro. It needs to get the offset of a field from the base of a struct. This is very hard to represent in current rust. The way vulkano gets around this is by forcing the type to implement Default
, and read a field later.
slightly modified code from vulkano:
let dummy = <$out>::default();
let dummy_ptr = (&dummy) as *const _;
let member_ptr = (&dummy.$member) as *const _;
let offset = member_ptr as usize - dummy_ptr as usize;
There is no reason we should have to construct the default value here, as the compiler knows the offset to the field at compile time. One idea I've had in the past is something like this:
let dummy = std::ptr::null::<StructName>();
let offset = &(*dummy).member as *const _ as usize;
However, this has two problems; first, it dereferences a null pointer. This is UB, but I don't think it should be UB because we dereference a null pointer, but rather that the member we later reference is referenced through a borrow, so that borrow points to an invalid value.
If we could make the rules around getting a raw pointer from a previously dereferenced value less restrictive, maybe we could make this pattern non UB, because all we're really doing is offsetting the raw pointer with some compile time constant value. Although, offsetting would also be UB because we're not inside the same allocated object(we're not inside any allocated object).
Another problem is that the member might call deref
, so the rule would have to restrict the usage of deref somehow.
A much simpler idea is to just have a built-in macro for getting the byte offset to a struct field.