FlyingFoX
FlyingFoX

Reputation: 3509

How to make method not require mutable self for locked Mutex?

Acessing a field of a struct for reading rust playpen:

use std::sync::Mutex;
#[deriving(Show)]
struct Test{
    a: uint,
}
impl Test{
    fn new() -> Test{
        Test { a: 0}
    }
    fn get(&self) -> uint {
        self.a
    }
}
fn main() {
    let t = Test{a: 42};
    let m = Mutex::new(Test::new());
    println!("Getting t: {} where t.a = {}", t.get(), t.a);
    {
        let m2 = m.lock();
        println!("m2.a = {}", m2.a); // works
        //println!("m2.get() = {}", m2.get()); // error: cannot borrow immutable local variable `m2` as mutable
    }
}

So in this case accessing the field m2.a works, but calling m2.get() requires m2 to be mutable although get does not mutate anything and is not declared to be mutating anything especially not &self.

To make this code work I could just declare m2 with let mut m2 = m.lock(); and everything works fine, but why do I need that mut here and is there a better way to call m2.get() without declaring m2 mutable in a similar way as it works for t that I declare as non mutable and which still permits me to call t.get().

Upvotes: 3

Views: 1591

Answers (2)

reem
reem

Reputation: 7246

This is due to a slight limitation of the Deref family of traits right now - namely implementing both Deref and DerefMut for the same type right now is slightly broken due to the behavior of autoderef for methods, namely, deref_mut is always called, even to get & references.

As a result, MutexGuard needs to be mutable for you to call methods which require &self. Usually, immutable uses of Mutex are rare, and RWLock, which supports both read and write locks separately, is better suited to this use case as it allows concurrent read locks.

Upvotes: 3

sellibitze
sellibitze

Reputation: 28117

Yeah, the compiler tends to prefer to call deref_mut over deref in case both are available. In your case, deref would suffice and work, but the implicit dereferencing machinery picks deref_mut instead and then complains about m2 not being mutable.

To add to what reem said, the lock object does implement both, Deref and DerefMut and if you don't need a mutable borrow, you can get the immutable one by explicitly reborrowing it immutably:

println!("m2.get() = {}", (&*m2).get());

and if this kind of access is all you need you can also write

let m2 = m.lock();
let m2 = &*m2;

which then allows

println!("m2.get() = {}", m2.get());

Upvotes: 4

Related Questions