opaque_pool/
pooled_mut.rs

1use std::fmt;
2use std::ops::{Deref, DerefMut};
3use std::pin::Pin;
4use std::ptr::NonNull;
5
6use crate::{ItemCoordinates, Pooled};
7
8/// Exclusive handle to a pooled item that prevents double-use through the type system.
9///
10/// `PooledMut<T>` provides exclusive ownership of an item stored in an [`OpaquePool`] and
11/// ensures that each handle can only be used once. Unlike [`Pooled<T>`], this type does not
12/// implement [`Copy`] or [`Clone`], making it impossible to accidentally create multiple
13/// handles to the same pooled item or use a handle after it has been consumed.
14///
15/// This is the recommended handle type for most use cases as it provides stronger safety
16/// guarantees and enables safe removal operations without requiring `unsafe` code.
17///
18/// # Key Features
19///
20/// - **Exclusive ownership**: Each handle represents unique access to one pooled item
21/// - **Single-use guarantee**: Cannot be copied, cloned, or reused after consumption
22/// - **Safe removal**: Removal methods consume the handle, preventing use-after-free
23/// - **Direct value access**: Implements [`std::ops::Deref`] and [`std::ops::DerefMut`]
24/// - **Mutable access**: Full read-write access to the stored value
25/// - **Conversion to shared**: Use [`into_shared()`](PooledMut::into_shared) to enable copying
26/// - **Pinning support**: Safe [`std::pin::Pin`] access for both `&T` and `&mut T`
27/// - **Type erasure**: Use [`erase()`](PooledMut::erase) to convert to `PooledMut<()>`
28/// - **Pointer access**: Raw pointer access via [`ptr()`](PooledMut::ptr) for advanced cases
29///
30/// # Safety Benefits
31///
32/// `PooledMut<T>` provides stronger compile-time guarantees than [`Pooled<T>`]:
33///
34/// - **No double-use**: Cannot be copied or cloned, preventing accidental reuse
35/// - **Safe removal**: Pool removal methods consume the handle, making reuse impossible
36/// - **Move semantics**: Handle is moved/consumed by operations, enforcing single use
37/// - **Type system enforcement**: Rust's ownership system prevents use-after-move errors
38///
39/// # Relationship with `Pooled<T>`
40///
41/// `PooledMut<T>` can be converted to [`Pooled<T>`] using [`into_shared()`](PooledMut::into_shared)
42/// when you need to share access across multiple references. This conversion is one-way;
43/// you cannot convert back from [`Pooled<T>`] to `PooledMut<T>`.
44///
45/// # Examples
46///
47/// ## Safe exclusive access and removal
48///
49/// ```rust
50/// use opaque_pool::OpaquePool;
51///
52/// let mut pool = OpaquePool::builder().layout_of::<String>().build();
53///
54/// // Insert and get exclusive handle
55/// // SAFETY: String matches the layout used to create the pool
56/// let mut item = unsafe { pool.insert("Hello".to_string()) };
57///
58/// // Direct access through Deref and DerefMut
59/// assert_eq!(&*item, "Hello");
60/// item.push_str(", World!");
61/// assert_eq!(&*item, "Hello, World!");
62///
63/// // Safe removal - handle is consumed, preventing reuse
64/// pool.remove_mut(item);
65/// // item is now moved and cannot be used again!
66/// ```
67///
68/// ## Converting to shared access
69///
70/// ```rust
71/// use opaque_pool::OpaquePool;
72///
73/// let mut pool = OpaquePool::builder().layout_of::<u64>().build();
74///
75/// // SAFETY: u64 matches the layout used to create the pool
76/// let exclusive = unsafe { pool.insert(42_u64) };
77///
78/// // Convert to shared handle for copying
79/// let shared = exclusive.into_shared();
80/// let shared_copy = shared; // Now can copy freely
81///
82/// assert_eq!(*shared_copy, 42);
83///
84/// // Removal now requires unsafe since multiple handles could exist
85/// // SAFETY: No other copies of the handle will be used after this call
86/// unsafe { pool.remove(&shared_copy) };
87/// ```
88///
89/// ## Pinning support
90///
91/// ```rust
92/// use std::pin::Pin;
93///
94/// use opaque_pool::OpaquePool;
95///
96/// let mut pool = OpaquePool::builder().layout_of::<String>().build();
97///
98/// // SAFETY: String matches the layout used to create the pool
99/// let item = unsafe { pool.insert("Pinned".to_string()) };
100///
101/// // Get pinned references safely
102/// let pinned_ref: Pin<&String> = item.as_pin();
103/// assert_eq!(&**pinned_ref, "Pinned");
104///
105/// pool.remove_mut(item);
106/// ```
107///
108/// # Thread Safety
109///
110/// This type has the same thread safety properties as [`Pooled<T>`]. It is thread-safe
111/// ([`Send`] + [`Sync`]) if and only if `T` implements [`Sync`]. When `T` is [`Sync`],
112/// the handle can be moved between threads or shared between threads safely.
113///
114/// [`OpaquePool`]: crate::OpaquePool
115pub struct PooledMut<T: ?Sized> {
116    /// Ensures this handle can only be returned to the pool it came from.
117    pub(crate) pool_id: u64,
118
119    pub(crate) coordinates: ItemCoordinates,
120
121    pub(crate) ptr: NonNull<T>,
122}
123
124impl<T: ?Sized> PooledMut<T> {
125    /// Creates a new `PooledMut<T>` handle.
126    ///
127    /// This method is intended for internal use by the pool implementation.
128    #[must_use]
129    pub(crate) fn new(pool_id: u64, coordinates: ItemCoordinates, ptr: NonNull<T>) -> Self {
130        Self {
131            pool_id,
132            coordinates,
133            ptr,
134        }
135    }
136
137    /// Returns a pointer to the inserted value.
138    ///
139    /// This provides access to the value stored in the pool. The owner of the handle has
140    /// exclusive access to the value and may both read and write and may create both `&` shared
141    /// and `&mut` exclusive references to the item.
142    ///
143    /// # Example
144    ///
145    /// ```rust
146    /// use std::alloc::Layout;
147    ///
148    /// use opaque_pool::OpaquePool;
149    ///
150    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
151    ///
152    /// // SAFETY: String matches the layout used to create the pool.
153    /// let mut item = unsafe { pool.insert("Hello".to_string()) };
154    ///
155    /// // Read data back using Deref.
156    /// assert_eq!(&*item, "Hello");
157    ///
158    /// // Can also write to it using DerefMut.
159    /// item.push_str(", World!");
160    /// assert_eq!(&*item, "Hello, World!");
161    /// ```
162    #[must_use]
163    #[inline]
164    pub fn ptr(&self) -> NonNull<T> {
165        self.ptr
166    }
167
168    /// Converts this exclusive handle to a shared handle.
169    ///
170    /// This allows multiple shared handles to exist to the same pooled item.
171    /// The returned [`Pooled<T>`] handle can be copied and shared, but removal
172    /// operations will require `unsafe` code again.
173    ///
174    /// # Example
175    ///
176    /// ```rust
177    /// use std::alloc::Layout;
178    ///
179    /// use opaque_pool::OpaquePool;
180    ///
181    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
182    ///
183    /// // SAFETY: String matches the layout used to create the pool.
184    /// let item = unsafe { pool.insert("Test".to_string()) };
185    ///
186    /// // Convert to shared handle.
187    /// let shared = item.into_shared();
188    ///
189    /// // Can now copy the handle.
190    /// let shared_copy = shared;
191    ///
192    /// // But removal requires unsafe again.
193    /// unsafe { pool.remove(&shared_copy) };
194    /// ```
195    #[must_use]
196    #[inline]
197    pub fn into_shared(self) -> Pooled<T> {
198        Pooled::new(self.pool_id, self.coordinates, self.ptr)
199    }
200
201    /// Erases the type information from this [`PooledMut<T>`] handle, returning a [`PooledMut<()>`].
202    ///
203    /// This is useful when you want to store handles of different types in the same collection
204    /// or pass them to code that doesn't need to know the specific type.
205    ///
206    /// The handle remains functionally equivalent and can still be used to remove the item
207    /// from the pool and drop it. The only change is the removal of the type information.
208    ///
209    /// # Example
210    ///
211    /// ```rust
212    /// use std::alloc::Layout;
213    ///
214    /// use opaque_pool::OpaquePool;
215    ///
216    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
217    ///
218    /// // SAFETY: String matches the layout used to create the pool.
219    /// let item = unsafe { pool.insert("Test".to_string()) };
220    ///
221    /// // Erase type information.
222    /// let erased = item.erase();
223    ///
224    /// // Cast back to original type for safe access.
225    /// // SAFETY: We know this contains a String.
226    /// let typed_ptr = erased.ptr().cast::<String>();
227    /// let value = unsafe { typed_ptr.as_ref() };
228    /// assert_eq!(value, "Test");
229    ///
230    /// // Can still remove the item.
231    /// pool.remove_mut(erased);
232    /// ```
233    #[must_use]
234    #[inline]
235    pub fn erase(self) -> PooledMut<()> {
236        PooledMut {
237            pool_id: self.pool_id,
238            coordinates: self.coordinates,
239            ptr: self.ptr.cast::<()>(),
240        }
241    }
242
243    /// Returns a pinned reference to the value stored in the pool.
244    ///
245    /// Since values in the pool are always pinned (they never move once inserted),
246    /// this method provides safe access to `Pin<&T>` without requiring unsafe code.
247    ///
248    /// # Example
249    ///
250    /// ```rust
251    /// use std::pin::Pin;
252    ///
253    /// use opaque_pool::OpaquePool;
254    ///
255    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
256    ///
257    /// // SAFETY: String matches the layout used to create the pool.
258    /// let handle = unsafe { pool.insert("hello".to_string()) };
259    ///
260    /// let pinned: Pin<&String> = handle.as_pin();
261    /// assert_eq!(pinned.len(), 5);
262    /// ```
263    #[must_use]
264    #[inline]
265    pub fn as_pin(&self) -> Pin<&T> {
266        // SAFETY: Values in the pool are always pinned - they never move once inserted.
267        // The pool ensures stable addresses for the lifetime of the pooled object.
268        unsafe { Pin::new_unchecked(&**self) }
269    }
270
271    /// Returns a pinned mutable reference to the value stored in the pool.
272    ///
273    /// Since values in the pool are always pinned (they never move once inserted),
274    /// this method provides safe access to `Pin<&mut T>` without requiring unsafe code.
275    ///
276    /// # Example
277    ///
278    /// ```rust
279    /// use std::pin::Pin;
280    ///
281    /// use opaque_pool::OpaquePool;
282    ///
283    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
284    ///
285    /// // SAFETY: String matches the layout used to create the pool.
286    /// let mut handle = unsafe { pool.insert("hello".to_string()) };
287    ///
288    /// let mut pinned: Pin<&mut String> = handle.as_pin_mut();
289    /// // Can use Pin methods or deref to &mut String
290    /// ```
291    #[must_use]
292    #[inline]
293    pub fn as_pin_mut(&mut self) -> Pin<&mut T> {
294        // SAFETY: Values in the pool are always pinned - they never move once inserted.
295        // The pool ensures stable addresses for the lifetime of the pooled object.
296        // We have exclusive access through &mut self, so this is safe.
297        unsafe { Pin::new_unchecked(&mut **self) }
298    }
299}
300
301impl<T: ?Sized> Deref for PooledMut<T> {
302    type Target = T;
303
304    /// Provides direct access to the value stored in the pool.
305    ///
306    /// This allows the handle to be used as if it were a reference to the stored value.
307    ///
308    /// # Example
309    ///
310    /// ```rust
311    /// use opaque_pool::OpaquePool;
312    ///
313    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
314    ///
315    /// // SAFETY: String matches the layout used to create the pool.
316    /// let string_handle = unsafe { pool.insert("hello".to_string()) };
317    ///
318    /// // Access string methods directly.
319    /// assert_eq!(string_handle.len(), 5);
320    /// assert!(string_handle.starts_with("he"));
321    /// ```
322    #[inline]
323    fn deref(&self) -> &Self::Target {
324        // SAFETY: The pooled handle is valid and contains initialized memory of type T.
325        // The handle ensures the underlying pool data remains alive during access.
326        unsafe { self.ptr.as_ref() }
327    }
328}
329
330impl<T: ?Sized> DerefMut for PooledMut<T> {
331    /// Provides direct mutable access to the value stored in the pool.
332    ///
333    /// This allows the handle to be used as if it were a mutable reference to the stored value.
334    ///
335    /// # Example
336    ///
337    /// ```rust
338    /// use opaque_pool::OpaquePool;
339    ///
340    /// let mut pool = OpaquePool::builder().layout_of::<String>().build();
341    ///
342    /// // SAFETY: String matches the layout used to create the pool.
343    /// let mut string_handle = unsafe { pool.insert("hello".to_string()) };
344    ///
345    /// // Mutate the string directly.
346    /// string_handle.push_str(" world");
347    /// assert_eq!(*string_handle, "hello world");
348    /// ```
349    #[inline]
350    fn deref_mut(&mut self) -> &mut Self::Target {
351        // SAFETY: The pooled handle is valid and contains initialized memory of type T.
352        // We have exclusive access through PooledMut, so mutable access is safe.
353        unsafe { self.ptr.as_mut() }
354    }
355}
356
357impl<T: ?Sized> fmt::Debug for PooledMut<T> {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        f.debug_struct("PooledMut")
360            .field("type_name", &std::any::type_name::<T>())
361            .field("pool_id", &self.pool_id)
362            .field("coordinates", &self.coordinates)
363            .field("ptr", &self.ptr)
364            .finish()
365    }
366}
367
368// SAFETY: PooledMut<T> is just a fancy exclusive reference, so its thread-safety is entirely
369// driven by the underlying type T and the presence of the `Sync` auto trait on it.
370unsafe impl<T: ?Sized + Sync> Send for PooledMut<T> {}
371
372// SAFETY: PooledMut<T> is just a fancy exclusive reference, so its thread-safety is entirely
373// driven by the underlying type T and the presence of the `Sync` auto trait on it.
374unsafe impl<T: ?Sized + Sync> Sync for PooledMut<T> {}
375
376#[cfg(test)]
377#[allow(
378    clippy::undocumented_unsafe_blocks,
379    clippy::multiple_unsafe_ops_per_block,
380    clippy::items_after_statements,
381    reason = "tests focus on succinct code and do not need to tick all the boxes"
382)]
383mod tests {
384    use std::mem::MaybeUninit;
385
386    use crate::OpaquePool;
387
388    #[test]
389    fn smoke_test() {
390        let mut pool = OpaquePool::builder().layout_of::<String>().build();
391
392        assert_eq!(pool.len(), 0);
393        assert!(pool.is_empty());
394
395        let pooled_mut_a = unsafe { pool.insert("Hello".to_string()) };
396        let pooled_mut_b = unsafe { pool.insert("World".to_string()) };
397        let pooled_mut_c = unsafe { pool.insert("Test".to_string()) };
398
399        assert_eq!(pool.len(), 3);
400        assert!(!pool.is_empty());
401        assert!(pool.capacity() >= 3);
402
403        unsafe {
404            assert_eq!(pooled_mut_a.ptr().as_ref(), "Hello");
405            assert_eq!(pooled_mut_b.ptr().as_ref(), "World");
406            assert_eq!(pooled_mut_c.ptr().as_ref(), "Test");
407        }
408
409        pool.remove_mut(pooled_mut_b);
410
411        let pooled_mut_d = unsafe { pool.insert("Updated".to_string()) };
412
413        unsafe {
414            assert_eq!(pooled_mut_a.ptr().as_ref(), "Hello");
415            assert_eq!(pooled_mut_c.ptr().as_ref(), "Test");
416            assert_eq!(pooled_mut_d.ptr().as_ref(), "Updated");
417        }
418
419        pool.remove_mut(pooled_mut_a);
420        let extracted = pool.remove_unpin_mut(pooled_mut_d);
421        assert_eq!(extracted, "Updated");
422        // We do not remove pooled_mut_c, leaving that up to the pool to clean up.
423    }
424
425    #[test]
426    fn into_shared() {
427        let mut pool = OpaquePool::builder().layout_of::<String>().build();
428
429        let pooled_mut = unsafe { pool.insert("Test".to_string()) };
430        let shared = pooled_mut.into_shared();
431
432        // Can copy the shared handle.
433        let shared_copy = shared;
434
435        unsafe {
436            assert_eq!(shared.ptr().as_ref(), "Test");
437            assert_eq!(shared_copy.ptr().as_ref(), "Test");
438        }
439
440        // Clean up with the shared handle (requires unsafe again).
441        unsafe {
442            pool.remove(&shared_copy);
443        }
444    }
445
446    #[test]
447    fn insert_with() {
448        let mut pool = OpaquePool::builder().layout_of::<String>().build();
449
450        let pooled_mut = unsafe {
451            pool.insert_with(|uninit: &mut MaybeUninit<String>| {
452                uninit.write(String::from("In-place initialized"));
453            })
454        };
455
456        unsafe {
457            assert_eq!(pooled_mut.ptr().as_ref(), "In-place initialized");
458        }
459
460        let extracted = pool.remove_unpin_mut(pooled_mut);
461        assert_eq!(extracted, "In-place initialized");
462    }
463
464    #[test]
465    fn erase() {
466        let mut pool = OpaquePool::builder().layout_of::<String>().build();
467
468        let pooled_mut = unsafe { pool.insert("Test".to_string()) };
469        let erased = pooled_mut.erase();
470
471        // Can still access the raw pointer.
472        unsafe {
473            let value = erased.ptr().cast::<String>().as_ref();
474            assert_eq!(value.as_str(), "Test");
475        }
476
477        // Can still remove the item.
478        pool.remove_mut(erased);
479    }
480
481    #[test]
482    fn deref() {
483        let mut pool = OpaquePool::builder().layout_of::<String>().build();
484
485        let mut pooled_mut = unsafe { pool.insert("Hello, World!".to_string()) };
486
487        // Test Deref trait - should be able to access the value directly
488        assert_eq!(&*pooled_mut, "Hello, World!");
489        assert_eq!(pooled_mut.len(), 13);
490        assert!(pooled_mut.starts_with("Hello"));
491
492        // Test DerefMut trait - should be able to mutate the value directly
493        pooled_mut.push_str(" Testing deref!");
494        assert_eq!(&*pooled_mut, "Hello, World! Testing deref!");
495
496        // Make sure the mutation persisted in the pool
497        unsafe {
498            assert_eq!(pooled_mut.ptr().as_ref(), "Hello, World! Testing deref!");
499        }
500
501        pool.remove_mut(pooled_mut);
502    }
503
504    #[test]
505    fn as_pin() {
506        let mut pool = OpaquePool::builder().layout_of::<String>().build();
507
508        let mut pooled_mut = unsafe { pool.insert("Pin test".to_string()) };
509
510        // Test as_pin() method
511        let pinned_ref = pooled_mut.as_pin();
512        assert_eq!(&**pinned_ref, "Pin test");
513
514        // Test as_pin_mut() method
515        let pinned_mut = pooled_mut.as_pin_mut();
516        // Can deref Pin<&mut T> to get &mut T
517        pinned_mut.get_mut().push_str(" - pinned mutation");
518
519        // Verify the mutation worked
520        assert_eq!(&*pooled_mut, "Pin test - pinned mutation");
521
522        pool.remove_mut(pooled_mut);
523    }
524
525    #[test]
526    fn deref_coercion_works() {
527        let mut pool = OpaquePool::builder().layout_of::<String>().build();
528
529        let pooled_mut = unsafe { pool.insert("Coercion test".to_string()) };
530
531        // Test that deref coercion works - we can pass PooledMut<String> where &str is expected
532        fn take_str_ref(s: &str) -> usize {
533            s.len()
534        }
535
536        let len = take_str_ref(&pooled_mut);
537        assert_eq!(len, 13);
538
539        pool.remove_mut(pooled_mut);
540    }
541
542    #[test]
543    fn deref_with_custom_type() {
544        #[derive(Debug, PartialEq)]
545        struct CustomType {
546            value: i32,
547            name: String,
548        }
549
550        impl CustomType {
551            fn new(value: i32, name: String) -> Self {
552                Self { value, name }
553            }
554
555            fn increment(&mut self) {
556                self.value += 1;
557            }
558
559            fn get_value(&self) -> i32 {
560                self.value
561            }
562        }
563
564        let mut pool = OpaquePool::builder().layout_of::<CustomType>().build();
565
566        let mut pooled_mut = unsafe { pool.insert(CustomType::new(42, "test".to_string())) };
567
568        // Test Deref with custom type
569        assert_eq!(pooled_mut.get_value(), 42);
570        assert_eq!(pooled_mut.name, "test");
571
572        // Test DerefMut with custom type
573        pooled_mut.increment();
574        assert_eq!(pooled_mut.get_value(), 43);
575
576        pool.remove_mut(pooled_mut);
577    }
578
579    #[test]
580    fn pin_methods_work() {
581        let mut pool = OpaquePool::builder().layout_of::<String>().build();
582
583        let mut pooled_mut = unsafe { pool.insert("Pin test".to_string()) };
584
585        // Test that as_pin() returns a proper Pin<&T>
586        let pin_ref = pooled_mut.as_pin();
587        assert_eq!(pin_ref.get_ref(), "Pin test");
588
589        // Test that as_pin_mut() returns a proper Pin<&mut T>
590        let pin_mut = pooled_mut.as_pin_mut();
591        assert_eq!(&**pin_mut, "Pin test");
592
593        // Test that Pin<&mut T> can be used for mutation
594        let pin_mut = pooled_mut.as_pin_mut();
595        pin_mut.get_mut().push_str(" - modified through pin");
596
597        // Verify the mutation worked
598        assert_eq!(&*pooled_mut, "Pin test - modified through pin");
599
600        pool.remove_mut(pooled_mut);
601    }
602}