Rust从互斥体访问Option

ulmd4ohb  于 2023-04-21  发布在  其他
关注(0)|答案(2)|浏览(126)

我在理解如何修改互斥体中的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());

然而,当我将值 Package 在Some中时,我不得不手动使用ref mut等(我从here中找到了它),因为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而不是它的原始值吗?

biswetbf

biswetbf1#

实际上,Mutex::lock在两种情况下都返回Result<MutexGuard, ..>。不过,这种类型有有趣的trait实现: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 => {},
}

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

hjzp0vay

hjzp0vay2#

除了返回MutexGuard而不是其原始值的Option之外,还有其他数据类型吗?
MutexGuard不能返回原始值,因为移动值会使互斥体无效。相反,它是一个 Package 器,提供了一个对原始值的可变 * 引用 *。
这并不是特定于OptionMutexGuardMutex::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是为您提供对选项内数据的可变访问所必需的。

相关问题