Description
It would be beneficial to low-level code to add methods like offset
, add
, sub
non NonNull<T>
, so that pointer offsetting can be done without converting back and forth to raw pointers. These methods would be unsafe
just like the corresponding raw pointer methods, return NonNull<T>
and have the same semantics.
These methods will work well because such pointer offsetting is only valid inside the same allocated object; when offsetting it is not allowed to "leave" the current object, it can because of that never result in a null pointer. (For example, LLVM documents that an inbounds GEP on a non-null pointer must not result in a null pointer.)
For this reason, the safety rules that need to be followed for correct use of <NonNull<T>>::offset
are the same as for <*mut T>::offset
, and the method can be offered on the same terms, as an unsafe method.
This is for the moment implemented in at least one crate - rawpointer
and was a necessary feature for using NonNull<T>
in ndarray
.
Example implementation
impl<T> NonNull<T> {
/// Use the same documentation as the corresponding raw pointer method
pub unsafe fn offset(self, i: isize) -> Self {
// offset must be inside the same allocated object or one past the end, and can that way
// never result in a null pointer
NonNull::new_unchecked(self.as_ptr().offset(i))
}
pub unsafe fn add(self, i: usize) -> Self {
NonNull::new_unchecked(self.as_ptr().add(i))
}
pub unsafe fn sub(self, i: usize) -> Self {
NonNull::new_unchecked(self.as_ptr().sub(i))
}
}
The drawback of these methods is that while raw pointer offset
has tricky requirements (offset inside the same allocation) due to the code generation back-end, the new nonnull offset
methods will add extra requirements on top of that (Rust-level value validity); that these two restrictions go hand in hand, is just a consequence of the current back-end. Would it be possible to imagine a "nicer" Rust that didn't have these UB traps for pointer offsetting?