Is it safe to cast `ptr::from_raw_parts()` to `*mut T`?

I'm talking when it was derived from a &mut reference.

Usually, it's safe to cast a const raw pointer derived from a mutable reference to a mut pointer. This can be used to share common code between &self and &mut self methods. For example:

impl<T: ?Sized> Something<T> {
    pub fn get_ptr(this: *const Self) -> *const T {
        // Just an example
        this as *const T
    }
    pub fn get(&self) -> &T { unsafe { &*Self::get_ptr(self) } }
    pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *(Self::get_ptr(self as *mut Self) as *mut T) } }
}

Is it also valid when the get_ptr() function compose the pointer using the nightly std::ptr::from_raw_parts() or that the cycle breaks there, and I need from_raw_parts_mut()?

A similar question applies to std::ptr::addr_of!() and std::ptr::addr_of_mut!().

If possible, I'd prefer sources from the documentation and not just what you think.

Thank you.

1 Like

(Tangentially, IIRC that |it: &mut T| -> *const T { it } was (is?) a dangerous coercion since it may involve an implicit it as &T coercion, i.e., a &*it reborrow which changes provenance)

1 Like

Interesting. Does this have a source?

Not currently -- this is the coercion bug @Yandros mentioned. In both your example and from_raw_parts you hit the coercion. They wish it would work though.

I think you can just put my_mut_ref as *mut in there for it to be ok though? (I was trying to find confirmation but I'm going to post this right now instead since you just asked for a citation.)

1 Like

Nice to know. Still, I would like to know whether that's valid with as *mut Self, where it's valid without from_raw_parts(). Updated the main post.

1 Like

Yeah, the main post is still an interesting question; I've currently been assuming that if a _mut variant exists, then there may be a reason, but I can't provide anything more concrete: I feel like we need to summon @RalfJung this time :slightly_smiling_face:

2 Likes

Under my proposed aliasing model, only the initial cast from a reference to a raw ptr matters -- that must be reference as *mut _. Thereafter, it does not matter if you use *mut* or *const as long as it's all raw pointers. This includes methods like ptr::from_raw_parts since they use only raw pointers. It also includes addr_of! as long as you only take the address of fields -- once you actually follow an indirection, that's a totally unrelated pointer.

However, that is just my proposed model. There is nothing official currently.

3 Likes

I'll clarify one point here, regarding ptr::addr_of{,_mut}!, since I find this distinction to be a bit sneaky and thus able to catch some people off guard:

This means that while the following is fine under that model:

let mut it = (42_i32, 27_i32);
let p = &mut it as *mut _; // shared read-write over `(i32, i32)`.
unsafe { 
    // instead of `addr_of_mut!`
    let snd = ptr::addr_of!( (*p).1 ) as *mut i32;
    *snd += (*p).0;
}

The following is not "fine":

let mut it = (42, 27);
// instead of `addr_of_mut!`
let snd = ptr::addr_of!( it.1 ) as *mut i32;
unsafe {
    *snd += it.0; // Error, mutating through a shared read-only
}

It think it's pretty obvious in hindsight, but it doesn't hurt to clarify. In that regard, addr_of_mut! is less error-prone, by checking the mut-accessiblity as if a &mut was being performed :slightly_smiling_face:

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.