rust-lang / libs-team

The home of the library team
Apache License 2.0
110 stars 18 forks source link

Add raw-pointer-to-reference conversion methods #342

Closed RalfJung closed 4 months ago

RalfJung commented 4 months ago

Proposal

Problem statement

With https://github.com/rust-lang/rust/issues/106116, we have methods for almost all casts/conversions one wants to do on references and pointers, to avoid as casts and prefix operators. Just one direction is missing: turning raw pointers into references. Here we have as_ref/as_mut, but they behave different from &*ptr/&mut *ptr: they return an Option and perform a null-check. (These are the only methods on raw pointers that perform a null check.)

Motivating examples or use cases

Some random examples from a quick grep of the rustc sources:

&mut *(out as *mut &mut dyn PrintBackendInfo)
&mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>)
&mut *(self.0 as *mut _)
&mut *(vec as *mut Vec<Library>)
&mut *(value as *mut T as *mut UnsafeCell<T>)
&mut *(s as *mut T).cast::<[T; 1]>()

Solution sketch

I propose to add methods as_ref_unchecked/as_mut_unchecked for direct raw-pointer-to-reference conversions.

impl<T> *const T {
  unsafe fn as_ref_unchecked<'a>(self) -> &'a T {
    &*self
  }
}
impl<T> *mut T {
  unsafe fn as_ref_unchecked<'a>(self) -> &'a T {
    &*self
  }
  unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T {
    &mut *self
  }
}

The examples above then become

out.cast::<&mut dyn PrintBackendInfo>().as_mut_unchecked()
state.cast::<&mut dyn FnMut(&[u8]) -> io::Result<()>>().as_mut_unchecked()
self.0.cast().as_mut_unchecked()
vec.cast::<Vec<Library>>().as_mut_unchecked()
ptr::from_mut(value).cast::<UnsafeCell<T>>().as_mut_unchecked()
ptr::from_mut(s).cast::<[T; 1]>().as_mut_unchecked()

Unfortunately, since the as_mut name is already taken, these are all longer than the prefix variants. But they can be read left-to-right which is an advantage.

Alternatives

If we do nothing, one has to write this to get the same behavior:

out.cast::<&mut dyn PrintBackendInfo>().as_mut().unwrap_unchecked()
state.cast::<&mut dyn FnMut(&[u8]) -> io::Result<()>>().as_mut().unwrap_unchecked()
self.0.cast().as_mut().unwrap_unchecked()
vec.cast::<Vec<Library>>().as_mut().unwrap_unchecked()
ptr::from_mut(value).cast::<UnsafeCell<T>>().as_mut().unwrap_unchecked()
ptr::from_mut(s).cast::<[T; 1]>().as_mut().unwrap_unchecked()

Links and related work

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

Second, if there's a concrete solution:

m-ou-se commented 4 months ago

Discussed in the libs-api meeting.

:+1:

Feel free to open a tracking issue and open a PR to rust-lang/rust to add it as an unstable feature.