DEV Community

Lori-Shu
Lori-Shu

Posted on

Beyond &self and &mut self: Rust’s Underrated Method Receivers

If you write Rust, you interact with &self and &mut self every single day. They are the backbone of idiomatic Rust, signaling at a glance whether a method reads from or mutates an instance.

However, Rust’s ownership and type system goes deeper than just standard references. Tucked inside the language specification is arbitrary self types—allowing specialized receivers like self: &Arc and self: &Mutex.

While rare in everyday application code, these self-types are power tools when architecting concurrent and multi-threaded systems.

The Standard Receivers: A Quick Refresh

Before diving into the exotic variants, here is how the standard receivers behave:

&self (Shared Reference): Borrow the instance immutably. Multiple callers can execute this method simultaneously without mutating state.

&mut self (Exclusive Reference): Borrow the instance mutably. Guarantees exclusive access to modify the object's internal fields.

self (By Value): Consume the instance, taking full ownership and moving it into the method.

The Power Receivers: &Arc and &Mutex

In concurrent Rust, state is frequently wrapped inside thread-safe smart pointers or synchronization primitives—most notably Arc<T> (Atomic Reference Counting) and Mutex<T>.

Normally, calling a method on an Arc requires cloning the Arc explicitly before calling a standard method:

// Traditional approach
let shared_node = Arc::new(MyNode::new());
MyNode::process_node(shared_node.clone());
Enter fullscreen mode Exit fullscreen mode

By explicit receiver typing, you can make the method itself demand the smart-pointer wrapper directly on self:

impl MyNode {
    // Requires the caller to pass a reference to the Arc wrapping this instance
    pub fn spawn_task(self: &Arc<Self>) {
        let clone = Arc::clone(self);
        tokio::spawn(async move {
            clone.do_work();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)