Today I found that running
mem::size_of::<&mut ()>()
returns 8. I expected that much, but have many cases where it would be desirable to have it 0 sized. After a short but flamed talk on #rust I came up with the next change to Rust:
RFC: Zero-Sized References
References to any type in rust are represented as a pointer. Usually the pointer is smaller and faster to move around. However for Zero-Sized Types that only have a single value (for example () ) moving around is a no-op, and can be optimized away. Reading the value is a no-op since it has only a single value anyway and therefore it carries no extra information. However currently the compiler can't optimize away the pointer from data structures.
Zero-Sized Types are useful for functions, lifetime guarantees and destructors, and references to them can be used to show these types "exist" (for references) or "you are the only one using it" (for mutable references). The actual value is meaningless and the representation should be optimized to be of size 0 as well. After all Rust tries to make code as fast and small as possible.
Issues
Are references == pointers?
The first issue I encountered is was a conflict in the definition of "Reference". From what I understood, a reference is a way to access the original value without moving it around. It being a pointer was just an implementation detail.
However some people took the definition of "Reference" as a "Safe Pointer". Under their definition the fact the representation is a pointer is an inseparable part of it being a reference.
If the definition is the second case, then this whole RFC is irrelevant.
*mut and void*, and safe pointers
void*, a pointer to an unknown type, is represented in rust as a *mut c_void. However there are cases where people use other representations for it, including *mut (). It might still be fine, but the other issue is they use &mut () as a safe pointer (granting ownership guarantees and non-null guarantees).
For non-null pointers there exists core::nonzero::NonZero, however it is unstable and therefore unusable with stable rust.
In addition, any optimizations to &mut () might break code written in this way. Even if it was wrong to write it like this, rust is trying to prevent code from breaking, and might decide not to add this optimization for their sake.
Even if the optimization will be accepted, there should be either warnings added about turning zero-sized references into zero-sized pointers ( &mut () as *mut () ) and make it return a random pointer (NULL?), or throw an error for converting these.
Inaccessible data
Sometimes the data exists at some address but is not accessible through normal dereference. For example &mut FILE might be used with FFI and contain valid data at some location.
The above example would better be represented as
struct FILE {file: *mut c_void}
But if valid usage of references to inaccessible data can be found there might be a need to implement an escape hatch from this optimization, to hint that the value is not Zero-Sized, possibly
impl !Sized for FILE;