[ NEWBIE ] Why rust allows data to be mutated here?

Hi Everyone,

i read that in order to mutate date in a struct i have to use &mut self, so i was experimenting..

When i am using a Mutex to guard data in a struct, rust allows the data to be updated, can anyone explain the logic behind it?

use std::sync::Mutex;

#[derive(Debug)]
struct Actor {
    name: Mutex<String>,
}

impl Actor {
    fn update(&self) {
        *self.name.lock().unwrap() = "new name".into();
    }
}

fn main() {
    let actor = Actor { name: Mutex::new("abcd".into()) };
    actor.update();
    dbg!(actor);
}

This is known as interior mutability. In reality &self means shared access and &mut self means unique access. In your case, mutation is safe even in the face of shared access due to the mutex. Check out this article:

https://limpet.net/mbrubeck/2019/02/07/rust-a-unique-perspective.html

1 Like

The logic is quite simple:

  • Mutex::lock returns Result<MutexGuard, _>, by unwrapping it you get MutexGuard.
  • Then, you dereference it; since it implements DerefMut, dereferencing first converts &mut MutexGuard<T> to &mut T, then dereferences the result.
  • After that, the result is used as a "place expression", i.e. in the left side of an assignment.

@alice , @Cerber-Ursi if i understand correctly (plz correct me if i am wrong) &self only affects data that cannot be dereferenced ?? let's say exterior data, that is not hidden behind Type ??

I don't really understand what you mean, but interior mutability requires a Cell-like type to separate the immutable reference and the mutable data. Examples of Cell-like types include:

  • Mutex
  • RwLock
  • RefCell
  • Cell

&self affects everything, but here you can get an owned (therefore mutable) MutexGuard by using &Mutex. That the whole point of such synchronization primitives, after all.

@alice let assume that i have the following type defined somewhere in the code base

// file1.rs
type CuteCatName = Mutex<String>;

then in another file i saw this code,

// file2.rs

#[derive(Debug)]
struct Actor {
    age: usize,
    name: CutCatName,
}

impl Actor {
    fn get_mut_ref(&mut self) -> &str {
        self.age += 1;
        self.name.get_mut().unwrap()
    }
}

here my first impression (without knowing the CuteCatName definition) will be that &mut self is used to update the age, and let's assume get_mut() is named something else that does not indicate that its a mutable reference. because the return value is an immutable ref &str.

why &str can be modified, and is there a convention or something intuitive to quickly find out if its immutable or not without looking through the code base for the definition ??

There isn't a convention to mark uses of interior mutability.

It cannot. If you return &mut str, then it can, and that is indicated by the &mut. However, if you returned a &CuteCatName, then you are correct that there's nothing in the signature to indicate that its contents can be mutated. But if you have a &str then that is always immutable.

1 Like

No, you don't have to have &mut self to mutate. The immutable/mutable reference distinction is a not-quite-accurate oversimplification. & is shared, and &mut is exclusive access.

You can mutate shared data, but such mutation needs extra care for thread safety. So &self can be entirely valid for mutation if you use a thread-safe construct with it. & does not guarantee immutability. It only allows itself to be shared.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.