rust-lang / libs-team

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

Add "as raw pointer" methods to `Box` #355

Open RalfJung opened 4 months ago

RalfJung commented 4 months ago

Proposal

Problem statement

When working with raw pointers, it can be necessary to obtain raw pointers to the contents of a container in a way that multiple such pointers can alias with each other arbitrarily. For Vec, we have Vec::as_mut_ptr for that purpose, and that aliasing model interaction is even documented nowadays. However, for Box, we have no such method. One has to instead write addr_of_mut!(*bx), which works because Box is a primitive and this does not go through DerefMut. (DerefMut would be creating a reference and thus defeat the aliasing point of this.)

Motivating examples or use cases

Here's an example of code that's wrong:

use std::ptr;

fn test(mut x: Box<[u8]>) { unsafe {
    //let mut x: Vec<u8> = x.into(); // adding this line makes it well-defined
    let ptr1 = x[0..10].as_ptr();
    let ptr2 = x.as_mut_ptr().add(5); // this was meant not to invalidate `ptr1`...
    ptr::copy(ptr1, ptr2, 10);
} }

fn main() {
    test(Box::new([0; 128]));
}

This came about in rustc itself due to a refactor that replaced Vec by Box.

Solution sketch

I'm not sure how to avoid the refactoring issue, since Box can't really have self methods, so as_mut_ptr on a Box<[T]> will always call the slice method and thus create an intermediate reference. But we could at least provide methods that do the right thing and are more discoverable than the addr_of_mut! trick:

impl<T> Box<T> {
  fn as_ptr(this: &Box<T>) -> *const T { addr_of!(**this) }
  fn as_mut_ptr(this: &mut Box<T>) -> *mut T { addr_of_mut!(**this) }
}

This would also complement the existing into_raw.

Alternatives

We could do nothing and instead just document the addr_of_mut! pattern somewhere in the Box type docs.

Links and related work

Vec has as_ptr/as_mut_ptr with aliasing model guarantees.

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:

RalfJung commented 4 months ago

It is worth noting that this is entirely independent of https://github.com/rust-lang/unsafe-code-guidelines/issues/326. It's about avoiding intermediate references on the way from Box to the raw pointer; references unambiguously have noalias so their presence can make a difference.

scottmcm commented 3 months ago

I think we should do this regardless of it can can be done with casts, like how we have https://doc.rust-lang.org/std/ptr/fn.from_ref.html.