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}