handle_trait 1.0.0

A trait for types that represent handles to shared resources
Documentation
//! The Handle trait for types that represent handles to shared resources.
//!
//! This crate provides the `Handle` trait, which identifies types that function as
//! handles to underlying resources. A handle's defining feature is that cloning it
//! results in a second value that accesses the same underlying resource, creating
//! what can be thought of as "entanglement"—modifications visible through one handle
//! appear in all other handles to the same resource.
//!
//! # Examples
//!
//! ```
//! use handle_trait::Handle;
//! use std::rc::Rc;
//! use std::cell::RefCell;
//!
//! let data = Rc::new(RefCell::new(42));
//! let handle1 = data.handle(); // More semantically clear than .clone()
//!
//! *data.borrow_mut() = 100;
//! assert_eq!(*handle1.borrow(), 100); // Both handles see the same value
//! ```

use std::rc::Rc;
use std::sync::{Arc, mpsc};

/// Indicates that this type is a handle to some underlying resource.
///
/// Types that implement `Handle` have the property that cloning them creates
/// a second handle to the same underlying resource, rather than creating an
/// independent copy. This creates "entanglement" between the handles—changes
/// made through one handle are visible through all other handles.
///
/// The `handle()` method is semantically equivalent to `clone()`, but its name
/// makes the code's intent clearer: we're creating another handle to the same
/// resource, not making an independent copy.
///
/// # Examples
///
/// ```
/// use handle_trait::Handle;
/// use std::sync::Arc;
/// use std::sync::atomic::{AtomicU32, Ordering};
///
/// let counter = Arc::new(AtomicU32::new(0));
/// let handle1 = counter.handle();
/// let handle2 = counter.handle();
///
/// handle1.fetch_add(1, Ordering::SeqCst);
/// handle2.fetch_add(1, Ordering::SeqCst);
///
/// assert_eq!(counter.load(Ordering::SeqCst), 2);
/// ```
pub trait Handle: Clone {
    /// Creates a new handle to the same underlying resource.
    ///
    /// This method is semantically equivalent to `clone()`, but makes the intent
    /// clearer: we're creating another handle to the same resource.
    ///
    /// # Examples
    ///
    /// ```
    /// use handle_trait::Handle;
    /// use std::rc::Rc;
    ///
    /// let rc = Rc::new(42);
    /// let handle = rc.handle();
    /// assert_eq!(Rc::strong_count(&rc), 2);
    /// ```
    #[inline]
    #[must_use = "creating a handle without using it has no effect"]
    fn handle(&self) -> Self {
        self.clone()
    }
}

// Implement Handle for shared references
impl<T: ?Sized> Handle for &T {}

// Implement Handle for reference-counted pointers
impl<T: ?Sized> Handle for Rc<T> {}
impl<T: ?Sized> Handle for Arc<T> {}

// Implement Handle for channel types that use internal reference counting
impl<T> Handle for mpsc::Sender<T> {}
impl<T> Handle for mpsc::SyncSender<T> {}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;
    use std::sync::atomic::{AtomicU32, Ordering};

    #[test]
    fn test_shared_reference_handle() {
        let value = 42;
        let ref1 = &value;
        let ref2 = ref1.handle();

        assert_eq!(*ref1, *ref2);
        assert_eq!(*ref1, 42);
    }

    #[test]
    fn test_rc_handle_entanglement() {
        let data = Rc::new(RefCell::new(vec![1, 2, 3]));
        let handle1 = data.handle();
        let handle2 = data.handle();

        // Verify strong count increased
        assert_eq!(Rc::strong_count(&data), 3);

        // Modify through one handle
        data.borrow_mut().push(4);

        // Verify all handles see the change
        assert_eq!(*handle1.borrow(), vec![1, 2, 3, 4]);
        assert_eq!(*handle2.borrow(), vec![1, 2, 3, 4]);
    }

    #[test]
    fn test_arc_handle_entanglement() {
        let counter = Arc::new(AtomicU32::new(0));
        let handle1 = counter.handle();
        let handle2 = counter.handle();

        // Verify strong count
        assert_eq!(Arc::strong_count(&counter), 3);

        // Increment through different handles
        counter.fetch_add(1, Ordering::SeqCst);
        handle1.fetch_add(1, Ordering::SeqCst);
        handle2.fetch_add(1, Ordering::SeqCst);

        // All handles see the same value
        assert_eq!(counter.load(Ordering::SeqCst), 3);
        assert_eq!(handle1.load(Ordering::SeqCst), 3);
        assert_eq!(handle2.load(Ordering::SeqCst), 3);
    }

    #[test]
    fn test_mpsc_sender_handle() {
        let (tx, rx) = mpsc::channel();
        let tx_handle = tx.handle();

        tx.send(1).unwrap();
        tx_handle.send(2).unwrap();

        assert_eq!(rx.recv().unwrap(), 1);
        assert_eq!(rx.recv().unwrap(), 2);
    }

    #[test]
    fn test_handle_is_clone() {
        let rc = Rc::new(42);
        let via_handle = rc.handle();
        let via_clone = rc.clone();

        // Both methods produce equivalent results
        assert_eq!(*via_handle, *via_clone);
        assert_eq!(Rc::strong_count(&rc), 3);
    }
}