movable_ref/pointer/self_ref.rs
1//! SelfRef type definition
2//!
3//! This module contains the main `SelfRef` type that represents a relative pointer.
4
5use crate::metadata::PointerRecomposition;
6use crate::offset::{Nullable, Offset, Ptr};
7use crate::pointer::unreachable::UncheckedOptionExt as _;
8use core::marker::PhantomData;
9use core::mem::MaybeUninit;
10use std::ptr::NonNull;
11
12/// It is always safe to cast between a
13/// `Option<NonNull<T>>` and a `*mut T`
14/// because they are the exact same in memory
15#[inline(always)]
16fn nn_to_ptr<T: ?Sized>(nn: Ptr<T>) -> *mut T {
17 unsafe { std::mem::transmute(nn) }
18}
19
20/// A pointer that stores offsets instead of addresses, enabling movable self-referential structures.
21///
22/// Unlike regular pointers that become invalid when data moves, `SelfRef` stores the relative
23/// distance to its target. This offset remains valid regardless of where the containing structure
24/// is moved in memory - stack, heap, or anywhere else.
25///
26/// The magic happens through the offset type `I`: use `i8` for tiny 1-byte pointers with ±127 byte
27/// range, `i16` for 2-byte pointers with ±32KB range, or larger types for bigger structures.
28///
29/// ```rust
30/// use movable_ref::SelfRef;
31///
32/// struct Node {
33/// value: String,
34/// self_ref: SelfRef<String, i16>, // 2 bytes instead of 8
35/// }
36///
37/// impl Node {
38/// fn new(value: String) -> Self {
39/// let mut node = Self {
40/// value,
41/// self_ref: SelfRef::null(),
42/// };
43/// node.self_ref.set(&mut node.value).unwrap();
44/// node
45/// }
46/// }
47///
48/// // Works everywhere - stack, heap, vectors
49/// let node = Node::new("test".into());
50/// let boxed = Box::new(node); // ✓ Moves to heap
51/// let mut vec = vec![*boxed]; // ✓ Moves again
52/// let value = unsafe { vec[0].self_ref.as_ref_unchecked() }; // ✓ Still valid
53/// ```
54///
55/// # Safety Considerations
56///
57/// `SelfRef` uses `unsafe` internally but provides safe setup methods. The main safety requirement
58/// is that once set, the relative positions of the pointer and target must not change. Moving
59/// the entire structure is always safe - it's only internal layout changes that cause issues.
60///
61/// Special care needed with packed structs: field reordering during drops can invalidate offsets.
62///
63/// Using `NonZero*` offset types with self-pointing (zero offset) is undefined behavior.
64pub struct SelfRef<T: ?Sized + PointerRecomposition, I: Offset = isize>(
65 I,
66 MaybeUninit<T::Components>,
67 PhantomData<*mut T>,
68);
69
70// Ergonomics and ptr like impls
71
72impl<T: ?Sized + PointerRecomposition, I: Offset> Copy for SelfRef<T, I> {}
73impl<T: ?Sized + PointerRecomposition, I: Offset> Clone for SelfRef<T, I> {
74 fn clone(&self) -> Self {
75 *self
76 }
77}
78
79impl<T: ?Sized + PointerRecomposition, I: Offset> Eq for SelfRef<T, I> {}
80impl<T: ?Sized + PointerRecomposition, I: Offset> PartialEq for SelfRef<T, I> {
81 fn eq(&self, other: &Self) -> bool {
82 std::ptr::eq(self, other)
83 }
84}
85
86/// Convert an offset into a `SelfRef`
87impl<T: ?Sized + PointerRecomposition, I: Offset> From<I> for SelfRef<T, I> {
88 fn from(i: I) -> Self {
89 Self(i, MaybeUninit::uninit(), PhantomData)
90 }
91}
92
93impl<T: ?Sized + PointerRecomposition, I: Nullable> SelfRef<T, I> {
94 /// Creates an unset relative pointer.
95 ///
96 /// This is the starting point for most `SelfRef` usage - create a null pointer,
97 /// then use `set()` to point it at your target data.
98 #[inline(always)]
99 pub fn null() -> Self {
100 Self(I::NULL, MaybeUninit::uninit(), PhantomData)
101 }
102
103 /// Checks if the pointer is unset.
104 #[inline(always)]
105 pub fn is_null(&self) -> bool {
106 self.0 == I::NULL
107 }
108}
109
110impl<T: ?Sized + PointerRecomposition, I: Offset> SelfRef<T, I> {
111 /// Sets the pointer to target the given value.
112 ///
113 /// Computes the offset from this `SelfRef`'s location to the target value.
114 /// Returns an error if the distance is too large for the offset type `I`.
115 ///
116 /// This is the safe way to establish the self-reference - it validates that
117 /// the offset fits before storing it.
118 ///
119 /// ```rust
120 /// use movable_ref::SelfRef;
121 /// let mut data = "hello".to_string();
122 /// let mut ptr: SelfRef<String, i16> = SelfRef::null();
123 /// ptr.set(&mut data).unwrap(); // Now points to data
124 /// ```
125 #[inline]
126 pub fn set(&mut self, value: &mut T) -> Result<(), I::Error> {
127 self.0 = I::sub(value as *mut T as _, self as *mut Self as _)?;
128 self.1 = MaybeUninit::new(T::decompose(value));
129
130 Ok(())
131 }
132
133 /// Sets the pointer without bounds checking.
134 ///
135 /// Like `set()` but assumes the offset will fit in type `I`. Used when you've
136 /// already validated the distance or are reconstructing a known-good pointer.
137 ///
138 /// # Safety
139 ///
140 /// The offset between `value` and `self` must be representable in `I`.
141 /// `value` must not be null.
142 #[inline]
143 pub unsafe fn set_unchecked(&mut self, value: *mut T) {
144 self.0 = I::sub_unchecked(value as _, self as *mut Self as _);
145 self.1 = MaybeUninit::new(T::decompose(&*value));
146 }
147
148 /// Reconstructs the target pointer without null checking.
149 ///
150 /// # Safety
151 ///
152 /// The pointer must have been successfully set and the relative positions
153 /// of the pointer and target must not have changed since setting.
154 #[inline]
155 unsafe fn as_raw_unchecked_impl(&self) -> *const T {
156 nn_to_ptr(T::recompose(
157 NonNull::new(self.0.add(self as *const Self as *const u8)),
158 self.1.assume_init(),
159 ))
160 }
161
162 /// Reconstructs the target as a mutable raw pointer.
163 ///
164 /// # Safety
165 ///
166 /// Same as `as_raw_unchecked_impl`.
167 #[inline]
168 pub unsafe fn as_raw_unchecked(&mut self) -> *mut T {
169 self.as_raw_unchecked_impl() as _
170 }
171
172 /// Reconstructs the target as a `NonNull` pointer.
173 ///
174 /// # Safety
175 ///
176 /// Same as `as_raw_unchecked_impl`.
177 #[inline]
178 pub unsafe fn as_non_null_unchecked(&mut self) -> NonNull<T> {
179 T::recompose(
180 NonNull::new(self.0.add(self as *mut Self as *mut u8)),
181 self.1.assume_init(),
182 )
183 .unchecked_unwrap("Tried to use an unset relative pointer, this is UB in release mode!")
184 }
185
186 /// Reconstructs the target as an immutable reference.
187 ///
188 /// This is the most common way to access your self-referenced data.
189 ///
190 /// # Safety
191 ///
192 /// Same as `as_raw_unchecked_impl`. Standard reference aliasing rules apply.
193 #[inline]
194 pub unsafe fn as_ref_unchecked(&self) -> &T {
195 &*self.as_raw_unchecked_impl()
196 }
197
198 /// Reconstructs the target as a mutable reference.
199 ///
200 /// # Safety
201 ///
202 /// Same as `as_raw_unchecked_impl`. Standard reference aliasing rules apply.
203 #[inline]
204 pub unsafe fn as_mut_unchecked(&mut self) -> &mut T {
205 &mut *self.as_raw_unchecked()
206 }
207}
208
209macro_rules! as_non_null_impl {
210 ($self:ident) => {
211 if $self.is_null() {
212 None
213 } else {
214 T::recompose(
215 NonNull::new($self.0.add($self as *const Self as *const u8)),
216 $self.1.assume_init(),
217 )
218 }
219 };
220}
221
222impl<T: ?Sized + PointerRecomposition, I: Nullable> SelfRef<T, I> {
223 /// Reconstructs the target as a raw pointer, returning null if unset.
224 ///
225 /// # Safety
226 ///
227 /// If the pointer was set, the relative positions must not have changed.
228 /// For most pointer types this is safe, but may be undefined behavior
229 /// for some exotic pointer representations.
230 #[inline]
231 pub unsafe fn as_raw(&mut self) -> *mut T {
232 nn_to_ptr(self.as_non_null())
233 }
234
235 /// Reconstructs the target as a `NonNull` pointer, returning `None` if unset.
236 ///
237 /// # Safety
238 ///
239 /// If the pointer was set, the relative positions must not have changed.
240 #[inline]
241 pub unsafe fn as_non_null(&mut self) -> Ptr<T> {
242 as_non_null_impl!(self)
243 }
244
245 /// Reconstructs the target as an immutable reference, returning `None` if unset.
246 ///
247 /// # Safety
248 ///
249 /// Standard reference aliasing rules apply. If the pointer was set,
250 /// the relative positions must not have changed.
251 #[inline]
252 pub unsafe fn as_ref(&self) -> Option<&T> {
253 Some(&*as_non_null_impl!(self)?.as_ptr())
254 }
255
256 /// Reconstructs the target as a mutable reference, returning `None` if unset.
257 ///
258 /// # Safety
259 ///
260 /// Standard reference aliasing rules apply. If the pointer was set,
261 /// the relative positions must not have changed.
262 #[inline]
263 pub unsafe fn as_mut(&mut self) -> Option<&mut T> {
264 Some(&mut *self.as_non_null()?.as_ptr())
265 }
266}