struct MyType;
let n: NonNull<()> = NonNull::from(&MyType).cast();
let _m = unsafe { n.cast::<MyType>().as_ref() };
in this example I've created a pointer that initially points at MyType but then I've made it point to a unit...What does it even mean for it to point at a unit, does it mean that it points nowhere?
Also, when I cast it back to MyType; is it pointing at a completely new MyType or is it the MyType which we had before casting it to a unit? How does casting even work when there is no type information available at runtime....? thanks!
no, pointing at a () meant pointing to the memory location where a () is, the exact same way pointing at a MyType means pointing to the memory location where a MyType is, and pointing at a String means pointing to the memory location where a String is.
the MyType you were pointing to was a temporary variable, that went out of scope at the end of the statement it was declared in.
so the new reference is pointing to nothing. this would generally be undefined behavior.
This specific case is an exception to this rule because MyType is a ZST, so it is not actually UB. adding data to MyType would make it UB.
castign a pointer just changes the type of the pointer, it does nothing at runtime because types don't exist at runtime.
note that raw pointers do not need to be valid, so you can have a NonNull<String> pointing at a u8 just fine, or even a NonNull<()> pointing at where a MyType was before, even after that MyType went out of scope. what is not ok though, it to dereference the pointer(or turn it into a reference) if it is not pointing a a valid instance of the type
If this is what pointing to a byte is...
pointer
|
V
+---+---+---+---+---+---+---+---+
| | | | | | | | |
+---+---+---+---+---+---+---+---+
^
|
pointer + size_of::<u8>()
...then this is what pointing to a unit (or your likewise zero-sized MyType) is.
pointer
|
V
+---+---+---+---+---+---+---+---+
| | | | | | | | |
+---+---+---+---+---+---+---+---+
^
|
pointer + size_of::<()>()
There are a bunch of special cases around zero-sized types (ZSTs). For example, a lot of aliasing and dereferencing requirements don't apply, as there are no actual bytes of memory involved.
@Morgane55440 answered this well, I'll just point out that casting pointers does not create new values at the pointed-to location.
That said, it's always possible to "read" the zero bytes of a ZST, because ZSTs are special. You could have used NonNull::<MyType>::dangling() instead of ::from(&MyType) and the code would still be okay, because ZSTs. Whether any reads you make would be "the same MyType" is more of a philosophical question than anything IMO.