#75091 was introduced with the following API. Let 's address the open questions.
impl<T: ?Sized> *mut T {
pub fn set_ptr_value(mut self, val: *mut u8) -> Self;
}
impl<T: ?Sized> *const T {
pub fn set_ptr_value(mut self, val: *const u8) -> Self;
}
This API has been found to be difficult to grasp. I believe this is because it is actually suboptimal. The operation uses the provenance and pointer value of the argument, and metadata of self
. To me, this is the wrong way around. The 'object identity' of the result is provided by the argument. A better formulation would be to have it be provided be the receiver. Thus, I would propose to change it like so:
impl<T: ?Sized> *mut T {
pub fn cast_to<U: ?Sized>(self, val: *mut U) -> *mut U;
// With obvious parallel to:
// pub const fn cast<U>(self) -> *mut U;
}
impl<T: ?Sized> *const T {
pub fn cast_to<U: ?Sized>(self, val: *const U) -> *const U;
// pub const fn cast<U>(self) -> *const U;
}
Note that this has the effect of 'resolving' the outstanding question by rather eliminating the need to agree on a single 'address'-pointer entirely.
A further comment asks if pointer metadata would make this method redundant. I don't think that this is the case. Firstly, the methods do not mention Metadata
at all. Secondly, this can be chained while splitting and recombination is needlessly hard to read. Let's compare actual usage:
Currently:
//! library/alloc/src/rc.rs: 896
// Reverse the offset to find the original RcBox.
let rc_ptr = unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) };
With proposed change in API:
//! library/alloc/src/rc.rs: 896
// Reverse the offset to find the original RcBox.
let rc_ptr = unsafe { (ptr as *mut u8).offset(-offset).cast_to(ptr as *mut RcBox<T>) };
Using metadata
introduces new complexity with no readability benefit:
//! library/alloc/src/rc.rs: 896
// Reverse the offset to find the original RcBox.
let rc_ptr = unsafe {
let addr = (ptr as *mut u8).offset(-offset) as *mut ();
// Note: since metadata is not variant, we need this cast anyways.
let meta = ptr::metadata(ptr.cast_to(ptr as *mut RcBox<T>));
ptr::from_raw_parts_mut(addr, meta);
}
I also have at least two distinct use cases in crates that require the use of set_ptr_value
, but not the full metadata interface. I'd be very glad if they could transition to a sanctioned way of modifying unsized pointers quickly rather than have to wait on the many, many more open questions on ptr_metadata
.
-
A custom, macro-based equivalent of
unsize
(Another feature with not expectation of quick stabilization) so that no-std smart-pointers can hold dyn-traits, which is highly beneficial for embedded due to a number of reasons. (Ask if further elaboration is beneficial). - A struct around
T: Read
that is optionallydyn Seek
/dyn BufRead
with no static trait bound on those. Rather, onlyfn as_seek(&mut self) -> Option<&mut dyn Seek>
is offered. This is easily achieved by storing an additional field of typeOption<*mut dyn Seek>
in the struct and setting the pointer address to the value field's address on callingas_seek
.