为什么我不能将借来的值放在 Mutex 后面并将其传递给另一个线程?

Why can't I put a borrowed value behind a Mutex and pass it to another thread?

我正在尝试将借来的值放在 Mutex 后面,但我在使用借用检查器时遇到了问题。这是演示我遇到的问题的简化代码:

use std::{
    marker::PhantomData,
    sync::{Arc, Mutex},
};

struct Test<'a> {
    d: PhantomData<Mutex<&'a u8>>,
}

impl<'a> Test<'a> {
    pub fn new() -> Self {
        Test { d: PhantomData }
    }
    pub fn test(&'a self) {}
}

fn main() {
    let t = Arc::new(Test::new());
    let t2 = t.clone();
    std::thread::spawn(move || {
        t2.test();
    });
}

编译失败,出现以下错误

error[E0597]: `t2` does not live long enough
  --> src/main.rs:21:9
   |
19 |     let t2 = t.clone();
   |         -- lifetime `'1` appears in the type of `t2`
20 |     std::thread::spawn(move || {
21 |         t2.test();
   |         ^^-------
   |         |
   |         borrowed value does not live long enough
   |         argument requires that `t2` is borrowed for `'1`
22 |     });
   |     - `t2` dropped here while still borrowed

我猜编译器认为 t2 在调用 test() 时可能会被借用到其他地方。似乎如果我将 struct Test 中的 d 字段的类型修改为除 Mutex 之外的任何内容,例如 d: Option<&'a u8>,它就可以正常工作。 Mutex 有什么特别之处,因为它只是 UnsafeCell?

的包装器

What is so special about Mutex since it's just a wrapper around an UnsafeCell?

区别是variance.

&'a T 在生命周期内 是协变的 'a:你可以将生命周期较长的不可变引用强制转换为生命周期较短的引用,因为在需要 &'short T 的地方传递 &'long T 总是安全的。这就是代码编译时没有 UnsafeCell.

的原因

但是 UnsafeCell<&'a T>'a 中是 不变的 因为它具有内部可变性:如果您可以将 UnsafeCell<&'long T> 传递给采用 UnsafeCell<&'short T>,该代码可以将短期引用写入您的长期单元格。所以强迫 UnsafeCell 拥有不同的生命周期是不安全的。

(同样适用于任何允许您改变其包含的引用的类型,例如 Mutex<&'a T>&mut &'a T。)