Skip to content

Commit c1230e1

Browse files
committed
Auto merge of rust-lang#95249 - HeroicKatora:set-ptr-value, r=dtolnay
Refactor set_ptr_value as with_metadata_of Replaces `set_ptr_value` (rust-lang#75091) with methods of reversed argument order: ```rust impl<T: ?Sized> *mut T { pub fn with_metadata_of<U: ?Sized>(self, val: *mut U) -> *mut U; } impl<T: ?Sized> *const T { pub fn with_metadata_of<U: ?Sized>(self, val: *const U) -> *const U; } ``` By reversing the arguments we achieve several clarifications: - The function closely resembles `cast` with an argument to initialize the metadata. This is easier to teach and answers a long outstanding question that had restricted cast to `Sized` pointee targets. See multiples reviews of <rust-lang#47631> - The 'object identity', in the form of provenance, is now preserved from the receiver argument to the result. This helps explain the method as a builder-style, instead of some kind of setter that would modify something in-place. Ensuring that the result has the identity of the `self` argument is also beneficial for an intuition of effects. - An outstanding concern, 'Correct argument type', is avoided by not committing to any specific argument type. This is consistent with cast which does not require its receiver to be a 'raw address'. Hopefully the usage examples in `sync/rc.rs` serve as sufficient examples of the style to convince the reader of the readability improvements of this style, when compared to the previous order of arguments. I want to take the opportunity to motivate inclusion of this method _separate_ from metadata API, separate from `feature(ptr_metadata)`. It does _not_ involve the `Pointee` trait in any form. This may be regarded as a very, very light form that does not commit to any details of the pointee trait, or its associated metadata. There are several use cases for which this is already sufficient and no further inspection of metadata is necessary. - Storing the coercion of `*mut T` into `*mut dyn Trait` as a way to dynamically cast some an arbitrary instance of the same type to a dyn trait instance. In particular, one can have a field of type `Option<*mut dyn io::Seek>` to memorize if a particular writer is seekable. Then a method `fn(self: &T) -> Option<&dyn Seek>` can be provided, which does _not_ involve the static trait bound `T: Seek`. This makes it possible to create an API that is capable of utilizing seekable streams and non-seekable streams (instead of a possible less efficient manner such as more buffering) through the same entry-point. - Enabling more generic forms of unsizing for no-`std` smart pointers. Using the stable APIs only few concrete cases are available. One can unsize arrays to `[T]` by `ptr::slice_from_raw_parts` but unsizing a custom smart pointer to, e.g., `dyn Iterator`, `dyn Future`, `dyn Debug`, can't easily be done generically. Exposing `with_metadata_of` would allow smart pointers to offer their own `unsafe` escape hatch with similar parameters where the caller provides the unsized metadata. This is particularly interesting for embedded where `dyn`-trait usage can drastically reduce code size.
2 parents ee915c3 + d489ea7 commit c1230e1

File tree

4 files changed

+95
-88
lines changed

4 files changed

+95
-88
lines changed

library/alloc/src/rc.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ impl<T: ?Sized> Rc<T> {
895895

896896
// Reverse the offset to find the original RcBox.
897897
let rc_ptr =
898-
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) };
898+
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) };
899899

900900
unsafe { Self::from_ptr(rc_ptr) }
901901
}
@@ -1338,7 +1338,7 @@ impl<T: ?Sized> Rc<T> {
13381338
Self::allocate_for_layout(
13391339
Layout::for_value(&*ptr),
13401340
|layout| Global.allocate(layout),
1341-
|mem| (ptr as *mut RcBox<T>).set_ptr_value(mem),
1341+
|mem| mem.with_metadata_of(ptr as *mut RcBox<T>),
13421342
)
13431343
}
13441344
}
@@ -2264,7 +2264,7 @@ impl<T: ?Sized> Weak<T> {
22642264
let offset = unsafe { data_offset(ptr) };
22652265
// Thus, we reverse the offset to get the whole RcBox.
22662266
// SAFETY: the pointer originated from a Weak, so this offset is safe.
2267-
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) }
2267+
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) }
22682268
};
22692269

22702270
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.

library/alloc/src/sync.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,8 @@ impl<T: ?Sized> Arc<T> {
895895
let offset = data_offset(ptr);
896896

897897
// Reverse the offset to find the original ArcInner.
898-
let arc_ptr = (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset));
898+
let arc_ptr =
899+
(ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>);
899900

900901
Self::from_ptr(arc_ptr)
901902
}
@@ -1182,7 +1183,7 @@ impl<T: ?Sized> Arc<T> {
11821183
Self::allocate_for_layout(
11831184
Layout::for_value(&*ptr),
11841185
|layout| Global.allocate(layout),
1185-
|mem| (ptr as *mut ArcInner<T>).set_ptr_value(mem) as *mut ArcInner<T>,
1186+
|mem| mem.with_metadata_of(ptr as *mut ArcInner<T>),
11861187
)
11871188
}
11881189
}
@@ -1888,7 +1889,7 @@ impl<T: ?Sized> Weak<T> {
18881889
let offset = unsafe { data_offset(ptr) };
18891890
// Thus, we reverse the offset to get the whole RcBox.
18901891
// SAFETY: the pointer originated from a Weak, so this offset is safe.
1891-
unsafe { (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) }
1892+
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>) }
18921893
};
18931894

18941895
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.

library/core/src/ptr/const_ptr.rs

+44-41
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,50 @@ impl<T: ?Sized> *const T {
4848
self as _
4949
}
5050

51+
/// Use the pointer value in a new pointer of another type.
52+
///
53+
/// In case `val` is a (fat) pointer to an unsized type, this operation
54+
/// will ignore the pointer part, whereas for (thin) pointers to sized
55+
/// types, this has the same effect as a simple cast.
56+
///
57+
/// The resulting pointer will have provenance of `self`, i.e., for a fat
58+
/// pointer, this operation is semantically the same as creating a new
59+
/// fat pointer with the data pointer value of `self` but the metadata of
60+
/// `val`.
61+
///
62+
/// # Examples
63+
///
64+
/// This function is primarily useful for allowing byte-wise pointer
65+
/// arithmetic on potentially fat pointers:
66+
///
67+
/// ```
68+
/// #![feature(set_ptr_value)]
69+
/// # use core::fmt::Debug;
70+
/// let arr: [i32; 3] = [1, 2, 3];
71+
/// let mut ptr = arr.as_ptr() as *const dyn Debug;
72+
/// let thin = ptr as *const u8;
73+
/// unsafe {
74+
/// ptr = thin.add(8).with_metadata_of(ptr);
75+
/// # assert_eq!(*(ptr as *const i32), 3);
76+
/// println!("{:?}", &*ptr); // will print "3"
77+
/// }
78+
/// ```
79+
#[unstable(feature = "set_ptr_value", issue = "75091")]
80+
#[must_use = "returns a new pointer rather than modifying its argument"]
81+
#[inline]
82+
pub fn with_metadata_of<U>(self, mut val: *const U) -> *const U
83+
where
84+
U: ?Sized,
85+
{
86+
let target = &mut val as *mut *const U as *mut *const u8;
87+
// SAFETY: In case of a thin pointer, this operations is identical
88+
// to a simple assignment. In case of a fat pointer, with the current
89+
// fat pointer layout implementation, the first field of such a
90+
// pointer is always the data pointer, which is likewise assigned.
91+
unsafe { *target = self as *const u8 };
92+
val
93+
}
94+
5195
/// Changes constness without changing the type.
5296
///
5397
/// This is a bit safer than `as` because it wouldn't silently change the type if the code is
@@ -764,47 +808,6 @@ impl<T: ?Sized> *const T {
764808
self.wrapping_offset((count as isize).wrapping_neg())
765809
}
766810

767-
/// Sets the pointer value to `ptr`.
768-
///
769-
/// In case `self` is a (fat) pointer to an unsized type, this operation
770-
/// will only affect the pointer part, whereas for (thin) pointers to
771-
/// sized types, this has the same effect as a simple assignment.
772-
///
773-
/// The resulting pointer will have provenance of `val`, i.e., for a fat
774-
/// pointer, this operation is semantically the same as creating a new
775-
/// fat pointer with the data pointer value of `val` but the metadata of
776-
/// `self`.
777-
///
778-
/// # Examples
779-
///
780-
/// This function is primarily useful for allowing byte-wise pointer
781-
/// arithmetic on potentially fat pointers:
782-
///
783-
/// ```
784-
/// #![feature(set_ptr_value)]
785-
/// # use core::fmt::Debug;
786-
/// let arr: [i32; 3] = [1, 2, 3];
787-
/// let mut ptr = arr.as_ptr() as *const dyn Debug;
788-
/// let thin = ptr as *const u8;
789-
/// unsafe {
790-
/// ptr = ptr.set_ptr_value(thin.add(8));
791-
/// # assert_eq!(*(ptr as *const i32), 3);
792-
/// println!("{:?}", &*ptr); // will print "3"
793-
/// }
794-
/// ```
795-
#[unstable(feature = "set_ptr_value", issue = "75091")]
796-
#[must_use = "returns a new pointer rather than modifying its argument"]
797-
#[inline]
798-
pub fn set_ptr_value(mut self, val: *const u8) -> Self {
799-
let thin = &mut self as *mut *const T as *mut *const u8;
800-
// SAFETY: In case of a thin pointer, this operations is identical
801-
// to a simple assignment. In case of a fat pointer, with the current
802-
// fat pointer layout implementation, the first field of such a
803-
// pointer is always the data pointer, which is likewise assigned.
804-
unsafe { *thin = val };
805-
self
806-
}
807-
808811
/// Reads the value from `self` without moving it. This leaves the
809812
/// memory in `self` unchanged.
810813
///

library/core/src/ptr/mut_ptr.rs

+44-41
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,50 @@ impl<T: ?Sized> *mut T {
4747
self as _
4848
}
4949

50+
/// Use the pointer value in a new pointer of another type.
51+
///
52+
/// In case `val` is a (fat) pointer to an unsized type, this operation
53+
/// will ignore the pointer part, whereas for (thin) pointers to sized
54+
/// types, this has the same effect as a simple cast.
55+
///
56+
/// The resulting pointer will have provenance of `self`, i.e., for a fat
57+
/// pointer, this operation is semantically the same as creating a new
58+
/// fat pointer with the data pointer value of `self` but the metadata of
59+
/// `val`.
60+
///
61+
/// # Examples
62+
///
63+
/// This function is primarily useful for allowing byte-wise pointer
64+
/// arithmetic on potentially fat pointers:
65+
///
66+
/// ```
67+
/// #![feature(set_ptr_value)]
68+
/// # use core::fmt::Debug;
69+
/// let mut arr: [i32; 3] = [1, 2, 3];
70+
/// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
71+
/// let thin = ptr as *mut u8;
72+
/// unsafe {
73+
/// ptr = thin.add(8).with_metadata_of(ptr);
74+
/// # assert_eq!(*(ptr as *mut i32), 3);
75+
/// println!("{:?}", &*ptr); // will print "3"
76+
/// }
77+
/// ```
78+
#[unstable(feature = "set_ptr_value", issue = "75091")]
79+
#[must_use = "returns a new pointer rather than modifying its argument"]
80+
#[inline]
81+
pub fn with_metadata_of<U>(self, mut val: *mut U) -> *mut U
82+
where
83+
U: ?Sized,
84+
{
85+
let target = &mut val as *mut *mut U as *mut *mut u8;
86+
// SAFETY: In case of a thin pointer, this operations is identical
87+
// to a simple assignment. In case of a fat pointer, with the current
88+
// fat pointer layout implementation, the first field of such a
89+
// pointer is always the data pointer, which is likewise assigned.
90+
unsafe { *target = self as *mut u8 };
91+
val
92+
}
93+
5094
/// Changes constness without changing the type.
5195
///
5296
/// This is a bit safer than `as` because it wouldn't silently change the type if the code is
@@ -878,47 +922,6 @@ impl<T: ?Sized> *mut T {
878922
self.wrapping_offset((count as isize).wrapping_neg())
879923
}
880924

881-
/// Sets the pointer value to `ptr`.
882-
///
883-
/// In case `self` is a (fat) pointer to an unsized type, this operation
884-
/// will only affect the pointer part, whereas for (thin) pointers to
885-
/// sized types, this has the same effect as a simple assignment.
886-
///
887-
/// The resulting pointer will have provenance of `val`, i.e., for a fat
888-
/// pointer, this operation is semantically the same as creating a new
889-
/// fat pointer with the data pointer value of `val` but the metadata of
890-
/// `self`.
891-
///
892-
/// # Examples
893-
///
894-
/// This function is primarily useful for allowing byte-wise pointer
895-
/// arithmetic on potentially fat pointers:
896-
///
897-
/// ```
898-
/// #![feature(set_ptr_value)]
899-
/// # use core::fmt::Debug;
900-
/// let mut arr: [i32; 3] = [1, 2, 3];
901-
/// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
902-
/// let thin = ptr as *mut u8;
903-
/// unsafe {
904-
/// ptr = ptr.set_ptr_value(thin.add(8));
905-
/// # assert_eq!(*(ptr as *mut i32), 3);
906-
/// println!("{:?}", &*ptr); // will print "3"
907-
/// }
908-
/// ```
909-
#[unstable(feature = "set_ptr_value", issue = "75091")]
910-
#[must_use = "returns a new pointer rather than modifying its argument"]
911-
#[inline]
912-
pub fn set_ptr_value(mut self, val: *mut u8) -> Self {
913-
let thin = &mut self as *mut *mut T as *mut *mut u8;
914-
// SAFETY: In case of a thin pointer, this operations is identical
915-
// to a simple assignment. In case of a fat pointer, with the current
916-
// fat pointer layout implementation, the first field of such a
917-
// pointer is always the data pointer, which is likewise assigned.
918-
unsafe { *thin = val };
919-
self
920-
}
921-
922925
/// Reads the value from `self` without moving it. This leaves the
923926
/// memory in `self` unchanged.
924927
///

0 commit comments

Comments
 (0)