Transmuting integers to pointers is a largely unspecified operation. It is likely not equivalent to an as cast. Doing non-zero-sized memory accesses with a pointer constructed this way is currently considered undefined behavior.
And then it says:
If you specifically need to store something that is “either an integer or a pointer”, use *mut (): integers can be converted to pointers and back without any loss (via as casts or via transmute).
So this means I can't construct a pointer for a non-zero sized type T from an integer using transmute and otherwise can only do it via as. Why? Is this related to the meta data of the pointer? But if the type has the () meta data, why is this rule still applied? And I also think as maybe doesn't add meta data, either.
Another speculation is that this conversion violates the "layout must be the same" rule because raw pointers have different layouts than integers so using transmute here is UB behaviour.
I'm not certain what the author of that text was thinking of, but it's worth noting that on a 64 bit system, many of the bits of a pointer are unnecessary (no mainstream platform actually supports 18 exabytes of memory) and so may be used for metadata that fits inside the pointer itself. This is called pointer tagging. (You can also store metadata in the low bits of a pointer with alignment >1, with slightly different implications.) There are various uses for pointer tagging; in some cases you might want casting through an integer to preserve those bits, in other cases not.
as casts can do work like masking unused bits. transmute is dumb. Even if you are certain they do the same thing, there's simply no need to use something as crude as transmute for what the language already supports using a more precise built-in mechanism.
This is wholly new knowledge for me. I previously thought transmute is smarter. So one aspect is that the constructed pointers from integers can be completely invalid in terms of the address content. And as cast is an easy fix to align the address correctly. Thank you so much!
I think it is also relevant that Rust's memory model is not yet specified. The memory model defines what operations are UB. Until the memory model is specified, many operations are conservatively marked UB (currently UB things may become defined in a later version, but not the other way around).
Integer-to-pointer casts are a grey area because of pointer provenance.
I am not qualified to talk about that concept, but I can recommend Ralf Jung's blog post (that's where I heard from provenance for the first time):
I think what's actually happened there is that those docs haven't been updated to account for strict provenance.
AFAIK transmuteing an integer to a pointer is equivalent to
So read that function's documentation.
(And don't use a transmute to convert an integer to a pointer, use that function, because without_provenance and without_provenance_mut can be called from safe code.)
One additional reason why transmuting integers to pointers is unspecified in Rust may be that C also never specified the memory representation of pointers and only allows converting integers to pointers and vice versa with implementation-defined results.
C doesn't even require that a null pointer points to address 0, only that converting the constant expression 0 to a pointer results in a pointer that cannot point to any object or function, and converting that pointer back into an integer must yield a 0 again:
Rust is at least a bit more explicit in that regard: std::ptr::null()
Creates a null raw pointer.
[…]
The resulting pointer has the address 0.
I don't think that's right. on platforms that use a limited form of provenance like pointer coloring, a transmute might end up with those bits being reinterpreted as an address.
it's also worth noting that on platforms like CHERI, pointers may be significantly larger than their address, and thus such a transmute may simply fail.