I am currently looking at making the following code a bit less “problematic”:
#[allow(clippy::mut_from_ref)]
#[allow(invalid_reference_casting)]
unsafe fn unsafe_ref_mut(&self) -> &mut Self {
// Use read_volatile to prevent compiler to optimize assignment to the returned reference away
let const_ptr = std::ptr::read_volatile(&self) as *const Self;
let mut_ptr = const_ptr as *mut Self;
&mut *mut_ptr
}
pub(crate) unsafe fn set_kind(&self, kind: Kind) {
unsafe {
self.unsafe_ref_mut().kind = kind;
}
}
I did not write this code myself, but I did add the attributes to silence the compiler warnings. Turns out that the rust compiler really doesn’t want you to cast an immutable borrow to a mutable one and there’s also undefined behaviour involved somewhere. It seems to work, but I’d rather prefer to use a safer method.
What I came up with is the following:
fn as_mut_ptr(&self) -> *mut Self {
(self as *const Self).cast_mut()
}
pub(crate) unsafe fn set_kind(&self, kind: Kind) {
(*self.as_mut_ptr()).kind = kind;
}
First of all, I’m somewhat surprised that casting an immutable reference to a mutable pointer is perfectly safe in rust. The only thing unsafe seems to be dereferencing that pointer.
Therefore, my question is: Under what circumstances is this code safe, i.e., what would I need to write into a Safety
section in the documentation of the set_kind
function?
From my understanding:
- The pointer needs to be non-null and well aligned (I assume this I can always guarantee since only Rust code ever creates the struct that these methods reside in)
- No two threads may access the function at the same time
- The struct cannot be shared.
Are these all requirements? Am I missing something?