Rust 生存期子类型不适用于 Cell



给定一个类型为Vec<&'static str>的值,我可以自由地将其转换为Vec<&'r str>,因为'r'static的一个子区域。这似乎适用于大多数类型,例如Vec、成对等但是,它不适用于CellRefCell等类型。具体来说,down_vec编译,但down_cell不会:

use std::cell::Cell;
fn down_vec<'p, 'r>(x: &'p Vec<&'static str>) -> &'p Vec<&'r str> {
x
}

fn down_cell<'p, 'r>(x: &'p Cell<&'static str>) -> &'p Cell<&'r str> {
x
}

给出错误:

error[E0308]: mismatched types
--> src/lib.rs:9:5
|
9 |     x
|     ^ lifetime mismatch
|
= note: expected reference `&'p std::cell::Cell<&'r str>`
found reference `&'p std::cell::Cell<&'static str>`
note: the lifetime `'r` as defined on the function body at 8:18...
--> src/lib.rs:8:18
|
8 | fn down_cell<'p, 'r>(x: &'p Cell<&'static str>) -> &'p Cell<&'r str> {
|                  ^^
= note: ...does not necessarily outlive the static lifetime

为什么这对Cell不起作用?编译器如何跟踪它不起作用?有没有其他方法可以使其工作?

CellRefCell是不同的,因为它们允许通过共享引用改变内部值。

为了了解为什么这很重要,我们可以编写一个函数,该函数使用down_cell泄漏对释放内存的引用:

fn oops() -> &'static str {
let cell = Cell::new("this string doesn't matter");
let local = String::from("this string is local to oops");
let broken = down_cell(&cell);  // use our broken function to rescope the Cell
broken.set(&local);             // use the rescoped Cell to mutate `cell`
cell.into_inner()               // return a reference to `local`
}                                   // uh-oh! `local` is dropped here

oops不包含unsafe块,但它可以编译,因此为了防止访问释放的内存,编译器必须拒绝down_cell

类型级解释为什么会这样,是因为Cell<T>RefCell<T>包含UnsafeCell<T>,这使得它们在T不变,而Box<T>Vec<T>T中是协变的。

VecBox和其他类似容器的结构之所以可以协变,是因为这些容器需要&mut访问才能改变它们的内容,而&mut T本身在T中是不变的。你不能用down_vec来写一个像oops这样的函数——编译器不允许这样做。

引用

  • Rustonomicon 的子类型和方差章节
  • Rust 编译器如何知道 'Cell' 具有内部可变性?
  • 为什么链接生存期只对可变引用很重要?

相关内容

最新更新