为什么采用不可变 `&self` 的方法可以使用互斥量修改字段中的数据?

Why can method taking immutable `&self` modify data in field with mutex?

考虑以下代码(也在playground上):

use std::{collections::HashMap, sync::Mutex};

struct MyStruct {
    dummy_map: Mutex<HashMap<i64, i64>>,
}

impl MyStruct {
    pub fn new() -> Self {
        MyStruct {
            dummy_map: Mutex::new(Default::default()),
        }
    }

    pub fn insert(&self, key: i64, val: i64) { // <-- immutable &self
        self.dummy_map.lock().unwrap().insert(key, val); // <-- insert in dummy_map
    }
}

fn main() {
    let s = MyStruct::new();
    let key = 1;
    s.insert(key, 1);
    assert!(s.dummy_map.lock().unwrap().get(&key).is_some());
}

代码运行时没有恐慌,这意味着 insert 采用不可变的 &self,但它仍然可以插入到地图(包裹在 Mutex 中)。

这怎么可能?

insert 服用 &mut self 会更好吗?表示一个字段被修改...

如果 dummy_map 未包含在 Mutex 中,则代码无法编译(如我所料)。参见 this playground

mut 在 Rust 中有点用词不当,它实际上意味着“独占访问”(你需要能够改变一个值,但稍微更通用)。在这种情况下,您显然无法获得对 Mutex 本身的独占访问权(因为重点是在线程之间共享它),因此您也无法获得对 self 的独占访问权。但是,您可以获得对 Mutex 内数据的临时独占访问权,此时可以对其进行变异。有点像 CellRefCell 的情况。

A Mutex 是实现“内部可变性”的 Rust 类型之一(参见 the docs for Cell 讨论)。

简而言之,实现“内部可变性”的类型绕过了编译时所有权检查,支持运行时检查。在这种情况下,Mutex 通过确保只有一个线程可以使用其 lock() and try_lock() 方法访问数据来在运行时强制执行可变性规则。

两种锁定方法 return 一个拥有的 MutexGuard 可以分别通过 DerefDerefMut 特征提供对数据的只读和可变访问。

最后,这意味着需要mut的变量是return编辑的MutexGuard,而不是Mutex本身。