Rust 生命周期子类型不适用于 Cell
Rust lifetime subtyping doesn't work with Cell
给定类型 Vec<&'static str>
的值,我可以将其自由转换为 Vec<&'r str>
,因为 'r
是 'static
的子区域。这似乎适用于大多数类型,例如Vec
、对等。但是,它不适用于 Cell
或 RefCell
等类型。具体来说,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
?编译器如何跟踪它不起作用?有没有替代方案可以让它发挥作用?
Cell
和 RefCell
不同,因为它们允许通过共享引用更改内部值。
要了解为什么这很重要,我们可以编写一个使用 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
.
中 协变
Vec
、Box
和其他类似容器的结构可以协变的原因是因为这些容器需要 &mut
访问权限来改变它们的内容,而 &mut T
是本身在 T
内不变。您不能使用 down_vec
编写像 oops
这样的函数——编译器不允许这样做。
参考资料
- Rustonomicon 的 Subtyping and Variance 章节
给定类型 Vec<&'static str>
的值,我可以将其自由转换为 Vec<&'r str>
,因为 'r
是 'static
的子区域。这似乎适用于大多数类型,例如Vec
、对等。但是,它不适用于 Cell
或 RefCell
等类型。具体来说,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
?编译器如何跟踪它不起作用?有没有替代方案可以让它发挥作用?
Cell
和 RefCell
不同,因为它们允许通过共享引用更改内部值。
要了解为什么这很重要,我们可以编写一个使用 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
.
Vec
、Box
和其他类似容器的结构可以协变的原因是因为这些容器需要 &mut
访问权限来改变它们的内容,而 &mut T
是本身在 T
内不变。您不能使用 down_vec
编写像 oops
这样的函数——编译器不允许这样做。
参考资料
- Rustonomicon 的 Subtyping and Variance 章节