alias_ptr/lib.rs
1//! The `alias-ptr` crate supplies the [`AliasPtr`] type,
2//! which allows safely creating multiple pointers to the same heap-allocated memory,
3//! and (unsafely) freeing the memory without reference counting overhead.
4
5use std::ops::Deref;
6use std::ptr::NonNull;
7
8/// An untracked shared ownership pointer, pointing to heap memory manually freed.
9///
10/// The type `AliasPtr<T>` provides shared ownership of a value of type `T`,
11/// allocated in the heap. Invoking [`copy`][AliasPtr::copy] on `AliasPtr` produces
12/// a new `AliasPtr` instance, which points to the same allocation on the heap as the
13/// source `AliasPtr`. When you call `delete` on any of the copies,
14/// the value stored in that allocation is dropped,
15/// and all of the copies can no longer be safely dereferenced.
16///
17/// `AliasPtr` is primarily intended as an unsafe building block for safe abstractions,
18/// in order to avoid the runtime overhead of `Rc` or `Arc`
19/// in cases where the lifetimes are known statically.
20///
21/// Shared references in Rust disallow mutation by default, and [`AliasPtr`]
22/// is no exception: you cannot generally obtain a mutable reference to
23/// something inside an [`AliasPtr`]. If you need mutability, put a `Cell`/`RefCell`
24/// (not thread-safe), `Mutex`/`RwLock`/`Atomic` (thread-safe), or `UnsafeCell` (unsafe)
25/// inside the `AliasPtr`.
26///
27/// ## Usage
28///
29/// For each `T` on the heap, you are responsible for calling `delete()`
30/// on exactly one `AliasPtr` pointing to it,
31/// and not dereferencing it or its aliases after.
32///
33/// In Rust terms, `AliasPtr<T>` acts like a `&T` that allows dangling and deletion,
34/// a `Rc<T>` or `Arc<T>` with manual deletion,
35/// or like a more convenient raw pointer which is assumed to be valid.
36///
37/// In C++ terms, `AliasPtr<T>` operates like `T*` or `T const*`, with shared ownership over `T`,
38/// where the programmer decides which one to `delete`.
39///
40/// ## Thread Safety
41///
42/// Since `AliasPtr<T>` only exposes the same set of safe operations as a `&T`
43/// (`delete()` is unsafe),
44/// it would technically be sound to expose the same `Send`/`Sync` bounds as `&T`:
45/// `impl Send/Sync for AliasPtr<T> where T: Sync`.
46///
47/// However, I added additional `T: Send` bounds for both `impl Send`/`Sync` (matching `Arc`),
48/// as a lint to guard against sending or cloning an `AliasPtr<T>` to another thread,
49/// then calling `delete()` there.
50///
51/// If these bounds are inappropriate for your data structure, you can `unsafe impl Send/Sync for`
52/// your type containing `AliasPtr`.
53///
54/// ## Implementation
55///
56/// `AliasPtr<T>` has the same size as `&T`, and is interconvertible with a `Box<T>`.
57///
58/// `AliasPtr` wraps a raw pointer rather than a `&T`,
59/// because it's not legal to pass a `&` into `Box::from_raw()`,
60/// and a dangling `&` may be UB.
61/// See ["How to Dismantle an Atomic Bomb"](http://blog.pnkfx.org/blog/2021/03/25/how-to-dismantle-an-atomic-bomb/)
62/// for details.
63#[repr(transparent)]
64pub struct AliasPtr<T: ?Sized>(NonNull<T>);
65// PhantomData is not necessary to prevent leaking Box<&'stack U> variables.
66// Also read https://docs.rs/crate/ptr/0.2.2/source/src/lib.rs for reference.
67
68impl<T: ?Sized> Clone for AliasPtr<T> {
69 /// Copy the pointer without copying the underlying data.
70 fn clone(&self) -> Self {
71 Self(self.0)
72 }
73}
74
75// TODO add support for custom allocators?
76// Perhaps holding an allocator reference is inappropriate for a raw pointer workalike,
77// so add a PhantomData(A) and accept an A in a "delete_alloc" function?
78// Not sure.
79
80impl<T: ?Sized> From<Box<T>> for AliasPtr<T> {
81 fn from(item: Box<T>) -> Self {
82 // Safety: pointer is obtained from Box::into_raw().
83 unsafe { Self::from_raw(Box::into_raw(item)) }
84 }
85}
86
87impl<T: Sized> AliasPtr<T> {
88 /// Allocates memory on the heap and then places `x` into it.
89 ///
90 /// This doesn't actually allocate if `T` is zero-sized.
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// # use alias_ptr::AliasPtr;
96 /// let five = AliasPtr::new(5);
97 /// ```
98 pub fn new(x: T) -> AliasPtr<T> {
99 AliasPtr::from(Box::new(x))
100 }
101}
102
103impl<T: ?Sized> AliasPtr<T> {
104 /// Constructs an `AliasPtr` from a raw pointer.
105 ///
106 /// # Safety
107 ///
108 /// `p` must be non-null and valid (its target is readable and writable).
109 ///
110 /// In order for calling `delete()` to be sound,
111 /// `p` must be obtained from `Box::into_raw()`.
112 pub unsafe fn from_raw(p: *mut T) -> Self {
113 Self(NonNull::new_unchecked(p))
114 }
115
116 // TODO should some of these functions be turned into type-level functions
117 // to avoid clashing with Deref?
118
119 /// Copy the pointer without copying the underlying data.
120 /// (This is equivalent to calling `clone()`.
121 /// This type doesn't implement `Copy` to ensure all copies are explicit.)
122 pub fn copy(&self) -> Self {
123 self.clone()
124 }
125
126 /// Call the destructor of `T` and free the allocated memory.
127 ///
128 /// # Safety
129 ///
130 /// The `AliasPtr` must be derived from `Box::into_raw()` (from the global allocator).
131 /// After calling `delete()`, neither self nor aliasing pointers
132 /// can be safely dereferenced (a safe but unsound operation).
133 ///
134 /// This method should logically take `self` by move,
135 /// but that would unfortunately prevent `drop(&mut Parent)` from calling `delete(self)`.
136 /// `Drop` only provides `&mut Parent`, which doesn't allow moving fields out.
137 /// For discussion, see ["Re-use struct fields on drop"](https://internals.rust-lang.org/t/re-use-struct-fields-on-drop-was-drop-mut-self-vs-drop-self/8594).
138 pub unsafe fn delete(&mut self) {
139 Box::from_raw(self.0.as_ptr());
140 }
141
142 /// Provides a raw pointer to the data.
143 ///
144 /// The pointer is valid until delete() is called on the `this` or any of its aliases.
145 pub fn as_ptr(this: &Self) -> *const T {
146 this.0.as_ptr()
147 }
148}
149
150impl<T: ?Sized> Deref for AliasPtr<T> {
151 type Target = T;
152 fn deref(&self) -> &T {
153 // Safety: AliasPtr is always constructed from a Box,
154 // so can be dereferenced safely.
155 // It is the responsibility of the user to never delete() a AliasPtr
156 // then dereference it or its aliases afterwards.
157 unsafe { &*self.0.as_ptr() }
158 }
159}
160
161unsafe impl<T: ?Sized> Send for AliasPtr<T> where T: Send + Sync {}
162unsafe impl<T: ?Sized> Sync for AliasPtr<T> where T: Send + Sync {}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use std::cell::Cell;
168 use std::mem::size_of;
169
170 struct AliasedPair(AliasPtr<Cell<i32>>, AliasPtr<Cell<i32>>);
171
172 impl AliasedPair {
173 fn new(x: i32) -> AliasedPair {
174 let x = AliasPtr::new(Cell::new(x));
175 AliasedPair(x.copy(), x)
176 }
177 }
178
179 impl Drop for AliasedPair {
180 fn drop(&mut self) {
181 unsafe {
182 self.0.delete();
183 }
184 }
185 }
186
187 #[test]
188 fn test_option_size_of() {
189 assert_eq!(size_of::<usize>(), size_of::<AliasPtr<i32>>());
190 assert_eq!(size_of::<usize>(), size_of::<Option<AliasPtr<i32>>>());
191 }
192
193 #[test]
194 fn test_aliased_pair() {
195 let pair = AliasedPair::new(1);
196 pair.0.set(42);
197 assert_eq!(pair.1.get(), 42);
198 }
199
200 // /// Does not compile, as expected.
201 // fn f() -> AliasPtr<&'static i32> {
202 // let x = 1;
203 // let out = AliasPtr::new(&x) as AliasPtr<&'static i32>;
204 // out
205 // }
206}