Rust 从互斥锁中访问 Option

4

我很难理解如何在 Mutex 中修改 Option。

当没有 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等(我从这里找到了方法),因为lock().unwrap()返回的是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返回MutexGuard而不是原始值之外,还有哪些数据类型会这样做?

请注意,在StackOverflow上,当问题得到解决时,提问者应该接受最有价值的答案。这将把问题从未回答的列表中移除,为未来的访问者提供一个提示,告诉他们哪个答案是最好的(在原始发布者的眼中),并奖励编写它的志愿者一枚声望奖章。如果答案没有解决你的问题,你可能需要编辑问题以澄清。 - user4815162342
2个回答

15

实际上,无论哪种情况,Mutex::lock 都会返回 Result<MutexGuard, ..>。不过这个类型有一个有趣的特征实现:DerefDerefMut。它们允许使用 * 运算符进行显式解引用。考虑一下这个具有显式类型的例子:

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

4
除了返回 MutexGuard 而不是其原始值的 Option,还有其他数据类型吗? MutexGuard 无法返回原始值,因为移动该值将使互斥锁无效。相反,它是一个包装器,提供对原始值的可变 引用
这与 Option 没有任何关系,MutexGuardMutex::lock 始终返回的对象。例如,以下代码:
let m = Mutex::<bool>::new(false);
let () = m.lock().unwrap();

...将报告m.lock().unwrap()返回的类型是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需要让您对选项内部的数据进行可变访问。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接