#handle #clone #traits #arc #sharing

handle_trait

A trait for types that represent handles to shared resources

2 stable releases

1.1.0 Oct 27, 2025
1.0.0 Oct 23, 2025

#831 in Rust patterns


Used in eidetica

MIT license

13KB
119 lines

handle_trait

A Rust crate providing the Handle trait for types that represent handles to shared resources.

Overview

The Handle trait 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", where modifications visible through one handle appear in all other handles to the same resource.

This trait is based on the concept described in Niko Matsakis's blog post about the Handle trait.

Why Use Handle?

The handle() method is semantically equivalent to clone(), but makes the code's intent much clearer. When you see .handle(), you immediately know that:

  1. You're creating another reference to the same underlying resource
  2. Changes through one handle will be visible through all handles
  3. No deep copying or independent duplication is occurring

Compare:

let tx2 = tx.clone();  // Are we copying the channel? Creating a new one?
let tx2 = tx.handle(); // Clearly creating another handle to the same channel

Usage

Add this to your Cargo.toml:

[dependencies]
handle_trait = "1"

Using the Derive Macro

You can automatically implement Handle for your types using the derive macro:

use handle_trait::Handle;
use std::sync::Arc;

#[derive(Clone, Handle)]
struct MyHandle {
    inner: Arc<i32>,
}

let h1 = MyHandle { inner: Arc::new(42) };
let h2 = h1.handle(); // Creates another handle to the same resource

The derive macro works with generic types and where clauses:

use handle_trait::Handle;
use std::sync::Arc;

#[derive(Clone, Handle)]
struct GenericHandle<T> {
    inner: Arc<T>,
}

To disable the derive feature (if you only need the trait):

[dependencies]
handle_trait = { version = "1", default-features = false }

Trait Definition

trait Handle: Clone {
    fn handle(&self) -> Self {
        self.clone()
    }
}

That's it.

This is only for semantics, it doesn't do anything differently than Clone.

In fact here is 100% of the actual code, verbatim, for this entire crate:

pub trait Handle: Clone {
    #[inline]
    #[must_use = "creating a handle without using it has no effect"]
    fn handle(&self) -> Self {
        self.clone()
    }
}

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

impl<T: ?Sized> Handle for &T {}
impl<T: ?Sized> Handle for Rc<T> {}
impl<T: ?Sized> Handle for Arc<T> {}
impl<T> Handle for mpsc::Sender<T> {}
impl<T> Handle for mpsc::SyncSender<T> {}

Implementations

This crate provides Handle implementations for:

  • Shared references: &T
  • Reference-counted pointers: Rc<T> and Arc<T>
  • Channel endpoints: mpsc::Sender<T> and mpsc::SyncSender<T>

Note that Box<T> does not implement Handle because cloning a Box creates an independent copy, not a handle to the same resource.

Design Philosophy

The Handle trait exposes the semantic concept of sharing rather than operational details. This makes it clearer when types should implement it and encourages developers to use handle() instead of clone() for improved code clarity.

When a type implements Handle, it communicates to users that:

  • Cloning creates entanglement, not independence
  • The type manages shared access to an underlying resource
  • Multiple handles coordinate access to the same data

License

This project is licensed under the MIT License.

Acknowledgments

This crate is based on the Handle trait concept proposed by Niko Matsakis in his blog post "The Handle Trait".

Dependencies

~95KB