Rust 从互斥体访问 Option

Rust accessing Option from mutex

我无法理解如何修改 Mutex 中的选项。

没有Option的时候可以正常工作

let mut my_int = Arc::new(Mutex::new(5));

let my_int_clone = Arc::clone(&my_int);

thread::spawn(move || {
  let mut my_int = my_int_clone.lock().unwrap();
  *my_int += 1;
}).join();

let my_int_clone_print = Arc::clone(&my_int);
println!("Value: {}", my_int_clone_print.lock().unwrap());

但是,当我将值包装在 Some 中时,我不得不手动使用 ref mut 等(我从 here 中找到它)因为 lock().unwrap() returns MutexGuard,而不是 Option 本身。

let mut my_int = Arc::new(Mutex::new(Some(5)));

let my_int_clone = Arc::clone(&my_int);

thread::spawn(move || {
  let mut my_int = my_int_clone.lock().unwrap();

  match *my_int {
    Some(ref mut val) => {
      *val += 1;
    },
    None => {
      println!("Value is None. Doing nothing..");
    }
  }

}).join();

let my_int_clone_print = Arc::clone(&my_int);
println!("Value: {}", my_int_clone_print.lock().unwrap());

知道是哪个 Rust 概念导致的吗?还有除了 Option returns MutexGuard 而不是其原始值之外还有其他数据类型吗?

实际上,Mutex::lock returns Result<MutexGuard, ..> in both cases. Though, this type has interesting trait implementation: Deref and DerefMut。这些允许通过 * 运算符显式取消引用。考虑这个带有显式类型的例子:

let mutex = Mutex::new(1i32);
let mut guard: MutexGuard<'_, i32> = mutex.lock().unwrap();

// This dereferences to &mut i32 
// because assignment operator works with &mut self.
*guard = 2;

// Nevertheless, for an explicit borrowing you need &mut
// because otherwise it would be moved from the guard.
let inner: &mut i32 = &mut *guard;

当然,您也可以类似地使用 Option

let mutex = Mutex::new(Some(1i32));
let mut guard: MutexGuard<'_, Option<i32>> = mutex.lock().unwrap();

// Directly change inner value
*guard = Some(2);

// or use in match, notice &mut borrowing
match &mut *guard {
    Some(x) => *x += 1,
    None => {},
}

注意,最后一个匹配示例与您的示例完全相同,但语法略有不同。 Playground.

there any more data types besides Option which returns MutexGuard and not its original value?

MutexGuard 不能 return 原始值,因为移动该值会使互斥量无效。相反,它是一个包装器,提供对原始值的可变 reference

这绝不是 Option 特有的,MutexGuardMutex::lock 总是 return 的。例如,这段代码:

let m = Mutex::<bool>::new(false);
let () = m.lock().unwrap();

...会抱怨 m.lock().unwrap() 编辑的 return 类型是 std::sync::MutexGuard<'_, bool>.

MutexGuard 在引用不超过守卫的条件下给出对数据的访问。 *my_int += 1 有效是因为 MutexGuard 实现了 DerefMut,它告诉 * 运算符要处理的引用。 * 运算符与 Option 完美配合;例如:

let m = Mutex::<Option<i32>>::new(Some(0));
let mut my_int = m.lock().unwrap();
*my_int = Some(100);

匹配 *my_int 可以在没有 ref mut 的情况下完成,但是 *my_int 将复制选项(只要其内容为 Copy 就可以工作)并修改该值对选项本身没有影响。这也绝不是 MutexGuard 特有的,这就是匹配的工作原理。需要 ref mut 才能让您可变访问选项内的数据。