Подтип жизни Rust не работает с Cell - PullRequest
1 голос
/ 29 мая 2020

Учитывая значение типа Vec<&'static str>, я могу свободно преобразовать его в Vec<&'r str>, поскольку 'r является подобластью 'static. Кажется, это работает для большинства типов, например Vec, пар и c. Однако это не работает для таких типов, как 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? Как компилятор отслеживает, что он не работает? Есть ли альтернатива, которая может заставить его работать?

1 Ответ

3 голосов
/ 29 мая 2020

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. Вы не могли написать такую ​​функцию, как oops, используя down_vec - компилятор не разрешил бы этого.

Ссылки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...