blind_pool/pooled_mut.rs
1use std::fmt;
2use std::ops::{Deref, DerefMut};
3use std::pin::Pin;
4
5use crate::{BlindPool, Pooled, RawPooled};
6
7/// A mutable reference to a value stored in a [`BlindPool`].
8///
9/// This type provides automatic lifetime management for values in the pool with exclusive access.
10/// When the [`PooledMut`] instance is dropped, the value is automatically removed from the pool.
11///
12/// Unlike [`Pooled<T>`], this type does not implement [`Clone`] and provides exclusive access
13/// through [`DerefMut`], making it suitable for scenarios where mutable access is required
14/// and shared ownership is not needed.
15///
16/// # Thread Safety
17///
18/// [`PooledMut<T>`] implements thread safety traits conditionally based on the stored type `T`:
19///
20/// - **Send**: [`PooledMut<T>`] is [`Send`] if and only if `T` is [`Send`]. This allows moving
21/// pooled mutable references between threads when the referenced type can be moved between threads.
22///
23/// - **Sync**: [`PooledMut<T>`] does NOT implement [`Sync`] because it provides exclusive mutable
24/// access via [`DerefMut`]. Allowing multiple threads to share references to the same
25/// [`PooledMut<T>`] instance would violate Rust's borrowing rules and lead to data races.
26///
27/// # Example
28///
29/// ```rust
30/// use blind_pool::BlindPool;
31///
32/// let pool = BlindPool::new();
33/// let mut value_handle = pool.insert_mut("Test".to_string());
34///
35/// // Mutably access the value.
36/// value_handle.push_str(" - Modified");
37/// assert_eq!(*value_handle, "Test - Modified");
38///
39/// // Value is automatically cleaned up when handle is dropped.
40/// ```
41pub struct PooledMut<T: ?Sized> {
42 /// The inner data containing the actual pooled item and pool handle.
43 inner: PooledMutInner<T>,
44}
45
46/// Internal data structure that manages the lifetime of a mutably pooled item.
47struct PooledMutInner<T: ?Sized> {
48 /// The typed handle to the actual item in the pool.
49 pooled: RawPooled<T>,
50
51 /// A handle to the pool that keeps it alive as long as this item exists.
52 pool: BlindPool,
53}
54
55impl<T: ?Sized> PooledMut<T> {
56 /// Creates a new [`PooledMut<T>`] from a pooled item and pool handle.
57 ///
58 /// This is an internal constructor used by [`BlindPool::insert_mut`] and
59 /// [`BlindPool::insert_with_mut`].
60 #[must_use]
61 pub(crate) fn new(pooled: RawPooled<T>, pool: BlindPool) -> Self {
62 let inner = PooledMutInner { pooled, pool };
63 Self { inner }
64 }
65
66 /// Provides access to the internal raw pooled handle for type casting operations.
67 ///
68 /// This method is used internally by the casting macro system and should not be
69 /// used directly by user code.
70 #[doc(hidden)]
71 pub fn __private_cast_dyn_with_fn<U: ?Sized, F>(self, cast_fn: F) -> PooledMut<U>
72 where
73 F: FnOnce(&mut T) -> &mut U,
74 {
75 // We need to prevent the Drop from running on the original handle while still
76 // extracting its fields. This is safe because we're transferring ownership
77 // to the new handle.
78 let this = std::mem::ManuallyDrop::new(self);
79 // SAFETY: We are reading from ManuallyDrop wrapped value to transfer ownership.
80 // The field is guaranteed to be initialized and we prevent Drop from running.
81 let pooled = unsafe { std::ptr::read(&raw const this.inner.pooled) };
82 // SAFETY: We are reading from ManuallyDrop wrapped value to transfer ownership.
83 // The field is guaranteed to be initialized and we prevent Drop from running.
84 let pool = unsafe { std::ptr::read(&raw const this.inner.pool) };
85
86 // Cast the RawPooled to the trait object using the provided function
87 // SAFETY: The lifetime management logic of this pool guarantees that the target item is
88 // still alive in the pool for as long as any handle exists, which it clearly does.
89 let cast_pooled = unsafe { pooled.__private_cast_dyn_with_fn_mut(cast_fn) };
90
91 // Create the new PooledMut with the cast handle and the same pool
92 PooledMut {
93 inner: PooledMutInner {
94 pooled: cast_pooled,
95 pool,
96 },
97 }
98 }
99
100 /// Returns a pinned reference to the value stored in the pool.
101 ///
102 /// Since values in the pool are always pinned (they never move once inserted),
103 /// this method provides safe access to `Pin<&T>` without requiring unsafe code.
104 ///
105 /// # Example
106 ///
107 /// ```rust
108 /// use std::pin::Pin;
109 ///
110 /// use blind_pool::BlindPool;
111 ///
112 /// let pool = BlindPool::new();
113 /// let handle = pool.insert_mut("hello".to_string());
114 ///
115 /// let pinned: Pin<&String> = handle.as_pin();
116 /// assert_eq!(pinned.len(), 5);
117 /// ```
118 #[must_use]
119 #[inline]
120 pub fn as_pin(&self) -> Pin<&T> {
121 // Delegate to the underlying opaque_pool Pooled<T> as_pin implementation.
122 // This is safe and efficient since opaque_pool now provides safe as_pin.
123 self.inner.pooled.opaque_handle().as_pin()
124 }
125
126 /// Returns a pinned mutable reference to the value stored in the pool.
127 ///
128 /// Since values in the pool are always pinned (they never move once inserted),
129 /// this method provides safe access to `Pin<&mut T>` without requiring unsafe code.
130 ///
131 /// # Example
132 ///
133 /// ```rust
134 /// use std::pin::Pin;
135 ///
136 /// use blind_pool::BlindPool;
137 ///
138 /// let pool = BlindPool::new();
139 /// let mut handle = pool.insert_mut("hello".to_string());
140 ///
141 /// let mut pinned: Pin<&mut String> = handle.as_pin_mut();
142 /// // Can use Pin methods or deref to &mut String
143 /// ```
144 #[must_use]
145 #[inline]
146 pub fn as_pin_mut(&mut self) -> Pin<&mut T> {
147 // SAFETY: We have exclusive access through PooledMut and the pooled handle contains
148 // a valid value. We can safely create a mutable reference to convert the shared
149 // RawPooled to provide mutable access for our exclusive PooledMut.
150 let mutable_ref = unsafe { self.inner.pooled.ptr().as_mut() };
151 // SAFETY: Values in the pool are always pinned - they never move once inserted.
152 unsafe { Pin::new_unchecked(mutable_ref) }
153 }
154
155 /// Converts this exclusive [`PooledMut<T>`] handle into a shared [`Pooled<T>`] handle.
156 ///
157 /// This operation consumes the [`PooledMut<T>`] and returns a [`Pooled<T>`] that allows
158 /// multiple shared references to the same pooled value. Once converted, you can no longer
159 /// obtain exclusive (mutable) references to the value, but you can create multiple
160 /// shared references through cloning.
161 ///
162 /// The returned [`Pooled<T>`] maintains the same lifetime management semantics -
163 /// the value will be automatically removed from the pool when all references
164 /// (including the returned [`Pooled<T>`]) are dropped.
165 ///
166 /// # Example
167 ///
168 /// ```rust
169 /// use blind_pool::BlindPool;
170 ///
171 /// let pool = BlindPool::new();
172 /// let mut exclusive_handle = pool.insert_mut("Test".to_string());
173 ///
174 /// // Modify the value while we have exclusive access.
175 /// exclusive_handle.push_str(" - Modified");
176 /// assert_eq!(*exclusive_handle, "Test - Modified");
177 ///
178 /// // Convert to shared access.
179 /// let shared_handle = exclusive_handle.into_shared();
180 ///
181 /// // Now we can create multiple shared references.
182 /// let cloned_handle = shared_handle.clone();
183 /// assert_eq!(*shared_handle, "Test - Modified");
184 /// assert_eq!(*cloned_handle, "Test - Modified");
185 ///
186 /// // Value is automatically cleaned up when all handles are dropped.
187 /// ```
188 #[must_use]
189 #[inline]
190 pub fn into_shared(self) -> Pooled<T> {
191 // We need to prevent the Drop from running on the original handle while still
192 // extracting its fields. This is safe because we're transferring ownership
193 // to the new shared handle.
194 let this = std::mem::ManuallyDrop::new(self);
195 // SAFETY: We are reading from ManuallyDrop wrapped value to transfer ownership.
196 // The fields are guaranteed to be initialized and we prevent Drop from running.
197 let pooled = unsafe { std::ptr::read(&raw const this.inner.pooled) };
198 // SAFETY: We are reading from ManuallyDrop wrapped value to transfer ownership.
199 // The field is guaranteed to be initialized and we prevent Drop from running.
200 let pool = unsafe { std::ptr::read(&raw const this.inner.pool) };
201
202 // Create the new shared handle with the same pooled item and pool
203 Pooled::new(pooled, pool)
204 }
205}
206
207impl<T: Unpin> PooledMut<T> {
208 /// Moves the value out of the pool, returning it to the caller and consuming the [`PooledMut<T>`].
209 ///
210 /// This method extracts the value from the pool and transfers ownership to the caller.
211 /// The item is removed from the pool, but the value is not dropped - instead, it is
212 /// returned to the caller who becomes responsible for its lifetime management.
213 ///
214 /// This method is only available for types that implement [`Unpin`], as moving a value
215 /// out of the pool would break the pinning guarantee for `!Unpin` types.
216 ///
217 /// # Example
218 ///
219 /// ```rust
220 /// use blind_pool::BlindPool;
221 ///
222 /// let pool = BlindPool::new();
223 /// let mut pooled_string = pool.insert_mut("Hello".to_string());
224 ///
225 /// // Modify the value while it is in the pool.
226 /// pooled_string.push_str(" World");
227 /// assert_eq!(*pooled_string, "Hello World");
228 /// assert_eq!(pool.len(), 1);
229 ///
230 /// // Extract the value from the pool.
231 /// let extracted_string = pooled_string.into_inner();
232 /// assert_eq!(extracted_string, "Hello World");
233 /// assert_eq!(pool.len(), 0); // Pool is now empty.
234 ///
235 /// // The caller now owns the value and is responsible for its lifetime.
236 /// println!("Extracted: {}", extracted_string);
237 /// ```
238 #[must_use]
239 #[inline]
240 pub fn into_inner(self) -> T {
241 // We need to prevent the Drop from running on the original handle while still
242 // extracting the pooled item information to remove it from the pool.
243 let this = std::mem::ManuallyDrop::new(self);
244
245 // SAFETY: We are reading from ManuallyDrop wrapped value to access fields.
246 // The fields are guaranteed to be initialized and we prevent Drop from running.
247 let pooled = unsafe { std::ptr::read(&raw const this.inner.pooled) };
248 // SAFETY: We are reading from ManuallyDrop wrapped value to access fields.
249 // The field is guaranteed to be initialized and we prevent Drop from running.
250 let pool = unsafe { std::ptr::read(&raw const this.inner.pool) };
251
252 // Remove the item from the pool and move the value out.
253 // SAFETY: T: Unpin is guaranteed by the impl constraint, and this pooled handle
254 // is consumed by this operation, ensuring it cannot be used again.
255 unsafe { pool.remove_unpin(&pooled) }
256 }
257}
258
259impl<T: ?Sized> Deref for PooledMut<T> {
260 type Target = T;
261
262 /// Provides direct access to the value stored in the pool.
263 ///
264 /// This allows the handle to be used as if it were a reference to the stored value.
265 ///
266 /// # Example
267 ///
268 /// ```rust
269 /// use blind_pool::BlindPool;
270 ///
271 /// let pool = BlindPool::new();
272 /// let string_handle = pool.insert_mut("hello".to_string());
273 ///
274 /// // Access string methods directly.
275 /// assert_eq!(string_handle.len(), 5);
276 /// assert!(string_handle.starts_with("he"));
277 /// ```
278 #[inline]
279 fn deref(&self) -> &Self::Target {
280 // Delegate to the underlying opaque_pool Pooled<T> Deref implementation.
281 // This is safe and efficient since opaque_pool now provides safe Deref.
282 self.inner.pooled.opaque_handle()
283 }
284}
285
286impl<T: ?Sized> DerefMut for PooledMut<T> {
287 /// Provides direct mutable access to the value stored in the pool.
288 ///
289 /// This allows the handle to be used as if it were a mutable reference to the stored value.
290 ///
291 /// # Example
292 ///
293 /// ```rust
294 /// use blind_pool::BlindPool;
295 ///
296 /// let pool = BlindPool::new();
297 /// let mut string_handle = pool.insert_mut("hello".to_string());
298 ///
299 /// // Mutate the string directly.
300 /// string_handle.push_str(" world");
301 /// assert_eq!(*string_handle, "hello world");
302 /// ```
303 #[inline]
304 fn deref_mut(&mut self) -> &mut Self::Target {
305 // We cannot delegate to opaque_pool's DerefMut because our pooled handle is shared,
306 // but we provide exclusive access through PooledMut's ownership semantics.
307 // SAFETY: The pooled handle is valid and contains initialized memory of type T.
308 // We have exclusive ownership through PooledMut, so no other references can exist.
309 unsafe { self.inner.pooled.ptr().as_mut() }
310 }
311}
312
313impl<T: ?Sized> Drop for PooledMut<T> {
314 /// Automatically removes the item from the pool when the handle is dropped.
315 ///
316 /// This ensures that resources are properly cleaned up without requiring manual intervention.
317 #[inline]
318 fn drop(&mut self) {
319 // We have exclusive ownership, so we can safely remove the item from the pool.
320 // SAFETY: This pooled handle is being consumed by Drop, ensuring it cannot be used again.
321 unsafe {
322 self.inner.pool.remove(&self.inner.pooled.erase());
323 }
324 }
325}
326
327// SAFETY: PooledMut<T> can be Send if T is Send, because we can move the exclusive
328// mutable access between threads when T can be moved between threads.
329unsafe impl<T: ?Sized + Send> Send for PooledMut<T> {}
330
331// Note: PooledMut<T> does NOT implement Sync because it provides mutable access
332// via DerefMut. Allowing multiple threads to share references to the same
333// PooledMut<T> instance would violate Rust's borrowing rules and lead to data races.
334
335impl<T: ?Sized> fmt::Debug for PooledMut<T> {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 f.debug_struct("PooledMut")
338 .field("type_name", &std::any::type_name::<T>())
339 .field("ptr", &self.inner.pooled.ptr())
340 .finish()
341 }
342}
343
344impl<T: ?Sized> fmt::Debug for PooledMutInner<T> {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 f.debug_struct("PooledMutInner")
347 .field("type_name", &std::any::type_name::<T>())
348 .field("ptr", &self.pooled.ptr())
349 .field("pool", &self.pool)
350 .finish()
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use static_assertions::{assert_impl_all, assert_not_impl_any};
357
358 use super::PooledMut;
359 use crate::BlindPool;
360
361 #[test]
362 fn thread_safety_assertions() {
363 // PooledMut<T> should be Send if T is Send, but never Sync
364 assert_impl_all!(PooledMut<u32>: Send);
365 assert_impl_all!(PooledMut<String>: Send);
366 assert_impl_all!(PooledMut<Vec<u8>>: Send);
367
368 // PooledMut should NOT be Clone
369 assert_not_impl_any!(PooledMut<u32>: Clone);
370 assert_not_impl_any!(PooledMut<String>: Clone);
371 assert_not_impl_any!(PooledMut<Vec<u8>>: Clone);
372
373 // PooledMut should NOT be Sync (regardless of T's Sync status)
374 // We only test with types that are known to not be Sync to avoid type inference issues
375 use std::cell::RefCell;
376 use std::rc::Rc;
377 assert_not_impl_any!(PooledMut<RefCell<u32>>: Sync); // RefCell is not Sync
378 assert_not_impl_any!(PooledMut<Rc<u32>>: Sync); // Rc is not Sync
379
380 // With non-Send types, PooledMut should also not be Send
381 assert_not_impl_any!(PooledMut<Rc<u32>>: Send); // Rc is not Send
382
383 // RefCell<T> is Send if T is Send
384 assert_impl_all!(PooledMut<RefCell<u32>>: Send); // RefCell<u32> is Send
385
386 // PooledMut should implement Unpin
387 assert_impl_all!(PooledMut<u32>: Unpin);
388 assert_impl_all!(PooledMut<String>: Unpin);
389 assert_impl_all!(PooledMut<Vec<u8>>: Unpin);
390 }
391
392 #[test]
393 fn automatic_cleanup() {
394 let pool = BlindPool::new();
395
396 {
397 let _handle = pool.insert_mut(42_u32);
398 assert_eq!(pool.len(), 1);
399 }
400
401 // Item should be automatically removed after drop
402 assert_eq!(pool.len(), 0);
403 assert!(pool.is_empty());
404 }
405
406 #[test]
407 fn mutable_access() {
408 let pool = BlindPool::new();
409 let mut handle = pool.insert_mut("hello".to_string());
410
411 // Test mutable access
412 handle.push_str(" world");
413 assert_eq!(*handle, "hello world");
414
415 // Test that the modification persists
416 assert_eq!(handle.len(), 11);
417 }
418
419 #[test]
420 fn deref_and_deref_mut_work() {
421 let pool = BlindPool::new();
422 let mut handle = pool.insert_mut(vec![1, 2, 3]);
423
424 // Test Deref
425 assert_eq!(handle.len(), 3);
426 assert_eq!(*handle.first().expect("vec should not be empty"), 1);
427
428 // Test DerefMut
429 handle.push(4);
430 *handle.get_mut(0).expect("index 0 should exist") = 10;
431
432 assert_eq!(*handle, vec![10, 2, 3, 4]);
433 }
434
435 #[test]
436 fn works_with_drop_types() {
437 use std::sync::Arc;
438 use std::sync::atomic::{AtomicBool, Ordering};
439
440 struct DropTracker {
441 dropped: Arc<AtomicBool>,
442 }
443
444 impl Drop for DropTracker {
445 fn drop(&mut self) {
446 self.dropped.store(true, Ordering::Relaxed);
447 }
448 }
449
450 let pool = BlindPool::new();
451 let dropped = Arc::new(AtomicBool::new(false));
452
453 {
454 let _handle = pool.insert_mut(DropTracker {
455 dropped: Arc::clone(&dropped),
456 });
457 assert!(!dropped.load(Ordering::Relaxed));
458 }
459
460 // Item's Drop should have been called when pool handle was dropped
461 assert!(dropped.load(Ordering::Relaxed));
462 }
463
464 #[test]
465 fn double_remove_bug_reproduction() {
466 use std::task::{Context, Poll, Waker};
467
468 #[allow(
469 clippy::unused_async,
470 reason = "Need async fn to create Future for testing"
471 )]
472 async fn echo(val: u32) -> u32 {
473 val
474 }
475
476 let pool = BlindPool::new();
477 let mut future_handle = pool.insert_mut(echo(10));
478
479 // Create a context for polling
480 let waker = Waker::noop();
481 let mut context = Context::from_waker(waker);
482
483 // Poll the future
484 let pinned_future = future_handle.as_pin_mut();
485 match pinned_future.poll(&mut context) {
486 Poll::Ready(result) => {
487 assert_eq!(result, 10);
488 }
489 Poll::Pending => {
490 // Should not happen for this simple future
491 panic!("Future should complete immediately");
492 }
493 }
494
495 // The handle should be dropped normally here without panicking
496 }
497
498 #[test]
499 fn detailed_future_test() {
500 use std::task::{Context, Poll, Waker};
501
502 // A simple async function that should complete immediately
503 #[allow(
504 clippy::unused_async,
505 reason = "Need async fn to create Future for testing"
506 )]
507 async fn echo(val: u32) -> u32 {
508 val
509 }
510
511 let pool = BlindPool::new();
512
513 // Insert the future into the pool
514 let mut future_handle = pool.insert_mut(echo(42));
515 assert_eq!(pool.len(), 1);
516
517 // Create a context for polling
518 let waker = Waker::noop();
519 let mut context = Context::from_waker(waker);
520
521 // Poll the future through the pinned reference
522 let pinned_future = future_handle.as_pin_mut();
523 let result = match pinned_future.poll(&mut context) {
524 Poll::Ready(value) => value,
525 Poll::Pending => panic!("Simple future should complete immediately"),
526 };
527
528 assert_eq!(result, 42);
529 assert_eq!(pool.len(), 1); // Future should still be in pool
530
531 // Drop the handle - this should cleanly remove the future from pool
532 drop(future_handle);
533 assert_eq!(pool.len(), 0); // Should be removed now
534 }
535
536 #[test]
537 fn unpin_with_non_unpin_type() {
538 use std::marker::PhantomPinned;
539
540 // Create a type that is !Unpin
541 struct NotUnpin {
542 _pinned: PhantomPinned,
543 value: u32,
544 }
545
546 // Verify that NotUnpin is indeed !Unpin
547 assert_not_impl_any!(NotUnpin: Unpin);
548
549 // PooledMut<NotUnpin> should still be Unpin because the wrapper implements Unpin
550 // regardless of T's Unpin status - the pooled data is always pinned in place
551 assert_impl_all!(PooledMut<NotUnpin>: Unpin);
552
553 let pool = BlindPool::new();
554 let handle = pool.insert_mut(NotUnpin {
555 _pinned: PhantomPinned,
556 value: 42,
557 });
558
559 // Can access the value normally
560 assert_eq!(handle.value, 42);
561 }
562
563 #[test]
564 #[allow(
565 dead_code,
566 reason = "Macro-generated trait only used for casting in this test"
567 )]
568 fn casting_with_futures() {
569 use std::future::Future;
570 use std::task::{Context, Poll, Waker};
571
572 /// Custom trait for futures returning u32.
573 pub(crate) trait MyFuture: Future<Output = u32> {}
574
575 /// Blanket implementation for any Future<Output = u32>.
576 impl<T> MyFuture for T where T: Future<Output = u32> {}
577
578 // Generate casting methods for MyFuture.
579 crate::define_pooled_dyn_cast!(MyFuture);
580
581 #[allow(
582 clippy::unused_async,
583 reason = "Need async fn to create Future for testing"
584 )]
585 async fn echo(val: u32) -> u32 {
586 val
587 }
588
589 let pool = BlindPool::new();
590
591 // Create the future handle first, then cast it (separate operations)
592 let original_handle = pool.insert_mut(echo(10));
593 let mut future_handle = original_handle.cast_my_future();
594
595 // After casting, the pool should still have the item
596 assert_eq!(pool.len(), 1);
597
598 // Poll the future using the safe pinning method from PooledMut
599 let waker = Waker::noop();
600 let mut context = Context::from_waker(waker);
601
602 // Use the as_pin_mut method to get a properly pinned reference
603 let pinned_future = future_handle.as_pin_mut();
604 match pinned_future.poll(&mut context) {
605 Poll::Ready(result) => {
606 assert_eq!(result, 10);
607 }
608 Poll::Pending => {
609 panic!("Simple future should complete immediately");
610 }
611 }
612
613 assert_eq!(pool.len(), 1); // Should still be 1 after polling
614
615 // Drop should work fine
616 drop(future_handle);
617 assert_eq!(pool.len(), 0);
618 }
619
620 #[test]
621 fn explicit_double_drop_test() {
622 // Try to create a scenario where something might be dropped twice
623 let pool = BlindPool::new();
624 let handle = pool.insert_mut(42_u32);
625
626 // Ensure the pool has the item
627 assert_eq!(pool.len(), 1);
628 assert_eq!(*handle, 42);
629
630 // Normal drop should work
631 drop(handle);
632 assert_eq!(pool.len(), 0);
633 }
634
635 #[test]
636 fn into_shared_conversion() {
637 let pool = BlindPool::new();
638 let mut exclusive_handle = pool.insert_mut("Test".to_string());
639
640 // Modify the value while we have exclusive access
641 exclusive_handle.push_str(" - Modified");
642 assert_eq!(*exclusive_handle, "Test - Modified");
643 assert_eq!(pool.len(), 1);
644
645 // Convert to shared access
646 let shared_handle = exclusive_handle.into_shared();
647 assert_eq!(*shared_handle, "Test - Modified");
648 assert_eq!(pool.len(), 1); // Should still be 1 item
649
650 // Now we can create multiple shared references
651 let cloned_handle = shared_handle.clone();
652 assert_eq!(*shared_handle, "Test - Modified");
653 assert_eq!(*cloned_handle, "Test - Modified");
654 assert_eq!(pool.len(), 1); // Still 1 item
655
656 // Value is automatically cleaned up when all handles are dropped
657 drop(shared_handle);
658 assert_eq!(pool.len(), 1); // Still alive due to cloned_handle
659 assert_eq!(*cloned_handle, "Test - Modified"); // Still accessible
660
661 drop(cloned_handle);
662 assert_eq!(pool.len(), 0); // Now cleaned up
663 }
664
665 #[test]
666 fn into_shared_with_different_types() {
667 let pool = BlindPool::new();
668
669 // Test with a Vec
670 let mut vec_handle = pool.insert_mut(vec![1, 2, 3]);
671 vec_handle.push(4);
672 let shared_vec = vec_handle.into_shared();
673 assert_eq!(*shared_vec, vec![1, 2, 3, 4]);
674
675 // Test with a u64
676 let mut_handle = pool.insert_mut(42_u64);
677 let shared_u64 = mut_handle.into_shared();
678 assert_eq!(*shared_u64, 42);
679
680 assert_eq!(pool.len(), 2);
681 drop(shared_vec);
682 drop(shared_u64);
683 assert_eq!(pool.len(), 0);
684 }
685
686 #[test]
687 fn into_inner_basic() {
688 let pool = BlindPool::new();
689 let mut handle = pool.insert_mut("Test".to_string());
690
691 // Modify the value while it is in the pool
692 handle.push_str(" - Modified");
693 assert_eq!(*handle, "Test - Modified");
694 assert_eq!(pool.len(), 1);
695
696 // Extract the value from the pool
697 let extracted = handle.into_inner();
698 assert_eq!(extracted, "Test - Modified");
699 assert_eq!(pool.len(), 0); // Pool should be empty now
700 }
701
702 #[test]
703 fn into_inner_with_different_types() {
704 #[derive(Debug, Eq, PartialEq)]
705 struct TestStruct {
706 value: i32,
707 text: String,
708 }
709
710 let pool = BlindPool::new();
711
712 // Test with a Vec (which is Unpin)
713 let mut vec_handle = pool.insert_mut(vec![1, 2, 3]);
714 vec_handle.push(4);
715 let extracted_vec = vec_handle.into_inner();
716 assert_eq!(extracted_vec, vec![1, 2, 3, 4]);
717
718 // Test with a u64 (which is Unpin)
719 let u64_handle = pool.insert_mut(42_u64);
720 let extracted_u64 = u64_handle.into_inner();
721 assert_eq!(extracted_u64, 42);
722
723 // Test with a custom struct (which is Unpin by default)
724 let struct_handle = pool.insert_mut(TestStruct {
725 value: 100,
726 text: "hello".to_string(),
727 });
728 let extracted_struct = struct_handle.into_inner();
729 assert_eq!(
730 extracted_struct,
731 TestStruct {
732 value: 100,
733 text: "hello".to_string(),
734 }
735 );
736
737 assert_eq!(pool.len(), 0); // All items should be extracted
738 }
739
740 #[test]
741 fn into_inner_preserves_value_ownership() {
742 let pool = BlindPool::new();
743 let handle = pool.insert_mut("Hello World".to_string());
744
745 // Extract the value
746 let extracted = handle.into_inner();
747
748 // Verify the extracted value is fully owned and can be modified
749 let mut owned_string = extracted;
750 owned_string.push_str(" - Owned");
751 assert_eq!(owned_string, "Hello World - Owned");
752
753 assert_eq!(pool.len(), 0);
754 }
755
756 #[test]
757 fn into_inner_with_drop_types() {
758 use std::sync::Arc;
759 use std::sync::atomic::{AtomicBool, Ordering};
760
761 struct DropTracker {
762 dropped: Arc<AtomicBool>,
763 }
764
765 impl Drop for DropTracker {
766 fn drop(&mut self) {
767 self.dropped.store(true, Ordering::Relaxed);
768 }
769 }
770
771 let pool = BlindPool::new();
772 let dropped = Arc::new(AtomicBool::new(false));
773
774 let handle = pool.insert_mut(DropTracker {
775 dropped: Arc::clone(&dropped),
776 });
777
778 // Value should not be dropped yet
779 assert!(!dropped.load(Ordering::Relaxed));
780
781 // Extract the value - this should not drop it
782 let extracted = handle.into_inner();
783 assert!(!dropped.load(Ordering::Relaxed));
784
785 assert_eq!(pool.len(), 0); // Pool should be empty
786
787 // Value should only be dropped when we drop the extracted value
788 drop(extracted);
789 assert!(dropped.load(Ordering::Relaxed));
790 }
791
792 #[test]
793 fn into_inner_with_copy_types() {
794 let pool = BlindPool::new();
795
796 // Test with a Copy type
797 let handle = pool.insert_mut(42_i32);
798 let extracted = handle.into_inner();
799 assert_eq!(extracted, 42);
800
801 // Test with arrays (which are Copy if elements are Copy)
802 let array_handle = pool.insert_mut([1, 2, 3, 4]);
803 let extracted_array = array_handle.into_inner();
804 assert_eq!(extracted_array, [1, 2, 3, 4]);
805
806 assert_eq!(pool.len(), 0);
807 }
808
809 #[test]
810 fn into_inner_pool_count_updates() {
811 let pool = BlindPool::new();
812
813 // Insert multiple items
814 let handle1 = pool.insert_mut("First".to_string());
815 let handle2 = pool.insert_mut("Second".to_string());
816 let handle3 = pool.insert_mut("Third".to_string());
817
818 assert_eq!(pool.len(), 3);
819
820 // Extract one item
821 let extracted1 = handle1.into_inner();
822 assert_eq!(extracted1, "First");
823 assert_eq!(pool.len(), 2);
824
825 // Extract another item
826 let extracted2 = handle2.into_inner();
827 assert_eq!(extracted2, "Second");
828 assert_eq!(pool.len(), 1);
829
830 // Extract the last item
831 let extracted3 = handle3.into_inner();
832 assert_eq!(extracted3, "Third");
833 assert_eq!(pool.len(), 0);
834 assert!(pool.is_empty());
835 }
836}