I’m in the midst of designing an API around raw pointers. I want to follow the example of the stdlib as much as possible. I want to know how to name the method(s) that extract a raw pointer out of a container. It looks like there are two different ways it’s done:
Provide a single method, as_ptr() -> *mut T. The caller can cast/coerce to *const T if necessary.
Provide both as_ptr() -> *const T and as_mut_ptr() -> *mut T.
Here are the 7 instances of as_ptr() I found in the stdlib. Some return *mut T. Others return *const T and are paired with a matching as_mut_ptr(), except for CStr which is asymmetrical.
Unfortunately, 4 of these return *const T and 3 return *mut T. It’s as close as you can get to a 50/50 split.
What’s the rationale here? Is this just an unfortunate historical inconsistency, or is there some common factor between the two groups that I’m not seeing, that should lead me to choose one approach over the other?
If you have a &Cell/&RefCell, you still are allowed to mutate the inner value (as these are interior/shared mutability types). With &slice/&str/&CStr/&MaybeUninit, the reference truly is immutable and mutating the value behind it is UB plain and simple.
NonNull is the odd one out here, but it’s also a very small wrapper around *mut. Thus, &NonNull<_> is basically &*mut _ and getting the *mut out with as_ptr(&self) makes sense.
Use as_ptr(&self) to return a shared pointer, and as_mut_ptr(&mut self) to return a mutable pointer iff the shared pointer isn’t already mutable.
Oh, and the variance of *const and *mut is different. (Tbh, I just pray that if I get variance wrong, someone will notice or I’ll notice in testing, and I stay away from where it matters as much as possible)
I don’t think variance of a return type practically matters but I daren’t touch variance with a 39½ft pole.
There’s a simpler answer. The mut in a function name, per the rust guidelines, refers to whether self is &mut or &, so as_ptr is always as_ptr(&self) and as_mut_ptr is always as_mut_ptr(&mut self).
FWIW, this thread prompted me to write https://github.com/rust-lang/rust/pull/60443 : please be aware that writing to a *const T pointer returned from as_ptr is UB! Just because you can cast it to *mut in safe code doesn’t mean you can actually use that pointer for writes.