I've been following along the Learn Rust the Dangerous Way
and I see in third advance a snippet that double casts a Rust reference to a pointer, with the comment Note 1
for i in 0..ROUNDED_INTERACTIONS_COUNT/2 {
let mut position_Delta =
[mem::MaybeUninit::<__m128d>::uninit(); 3];
for m in 0..3 {
position_Delta[m].as_mut_ptr().write(
*(&position_Deltas[m].0
as *const f64 // <----- Note 1
as *const __m128d).add(i)
);
}
let position_Delta: [__m128d; 3] =
mem::transmute(position_Delta);
The attached note says
In Rust, we do the same thing. Rust casts primitive types using as. Note that there is one more cast in Rust than in C (Note 1). This is because the expression &x in Rust takes the address of xas a reference, but we want a raw pointer — so we cast first to a pointer, and then to a pointer of a different type.
So this lead me to consider 2 things,
Is this not considered to be UB?
Unsafe code sometimes has to deal with pointers that may dangle, may be misaligned, or may not point to valid data. A common case where this comes up are repr(packed) structs. In such a case, it is important to avoid creating a reference, as that would cause undefined behavior. This means the usual & and &mut operators cannot be used, as those create a reference -- even if the reference is immediately cast to a raw pointer, it's too late to avoid the undefined behavior[1]
Is the new &raw syntax in Rust 1.82.0 a valid alternative double cast in the code snippet above?
It's not UB to cast a reference to a raw pointer; this is a normal thing to do code. You can only cause UB by misusing the raw pointer in unsafe code.
It's not UB to cast a pointer to a different type, or even to read or write through that pointer — as long as the layout and validity invariants of the two types are compatible for the specific reinterpretation you are doing. So, for example, if you cast a &mut f64 to *mut [u8; 8], then you can freely use the result, because it’s valid to treat any sequence of 8 bytes as a f64. However, if you cast an &mut bool to *mut u8, then you must not write anything but 0 or 1 to that pointer, or you’ve violated the validity invariant of bool.
Is the new &raw syntax in Rust 1.82.0 a valid alternative double cast in the code snippet above?
&raw replaces the reference-to-raw-pointer cast but not the pointee type cast, but pointer::cast() is a more specific way to change the pointee type than as. So, using both of them,
Thanks for the clarification, I thought that converting references to pointers as a whole was an issue from the quote "even if the reference is immediately cast to a raw pointer, it's too late to avoid the undefined behavior", but i.g that is specific to the state of the pointer and the data which it is pointing and not a general rule.
I tried augmenting the code with something like this
for m in 0..3 {
position_Delta[m].as_mut_ptr().write(
*(&raw const position_Deltas[m].0 as *const __m128d
).add(i));a
}
Is this approach similar to the pointer::cast you provided I am assuming that as *const m128d is the pointee type cast you mentioned?