Почему несколько изменяемых ссылок делают невозможным присвоение члена структуры самой себе? [дубликат]

Aug 17 2020

Я не могу брать взаймы там, где думал, что могу. Я свел проблему к этому случаю:

struct A<'a> {
    borrow: &'a mut u8,
}

fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
    a.borrow = a.borrow;
}
error[E0623]: lifetime mismatch
 --> src/lib.rs:6:16
  |
5 | fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
  |                            -------------
  |                            |
  |                            these two types are declared with different lifetimes...
6 |     a.borrow = a.borrow;
  |                ^^^^^^^^ ...but data from `a` flows into `a` here

Кажется , что a.borrowесть пересечение 'bи 'cпоэтому не может быть гарантирована до сих пор срок службы 'c.

У меня нет реальной проблемы с этим, и я могу обойти это, сделав обе жизни одинаковыми, но почему это не требует проверки?


Кажется, что структуры не важны для демонстрации этой проблемы, и двойные заимствования легко это показывают.

У меня есть три функции, которые очень похожи, но мне было бы трудно понять, какая из них компилируется и какую ошибку выдаст некомпилируемые.

Простая универсальная функция:

fn only_borrow<T>(a: &mut T) {
    *a = *a;
}

приводит к ошибке:

error[E0507]: cannot move out of `*a` which is behind a mutable reference
 --> src/lib.rs:2:10
  |
2 |     *a = *a;
  |          ^^ move occurs because `*a` has type `T`, which does not implement the `Copy` trait

Включение дополнительного уровня косвенного обращения изменяет ошибку

fn only_borrow_double<T>(a: &mut &mut T) {
    *a = *a;
}
error[E0623]: lifetime mismatch
 --> src/lib.rs:2:10
  |
1 | fn only_borrow_double<T>(a: &mut &mut T) {
  |                             -----------
  |                             |
  |                             these two types are declared with different lifetimes...
2 |     *a = *a;
  |          ^^ ...but data from `a` flows into `a` here

Отказ от предполагаемого срока службы может исправить ошибку:

fn working_double<'b, T>(a: &'b mut &'b mut T) {
    *a = *a;
}

Ответы

Emoun Aug 18 2020 at 09:59

Вам нужно будет взглянуть на свои жизни 'bи 'c:

  • &'b mut ...означает, что у вас есть ссылка, которая действительна и действительна в течение «времени» 'b.
  • A<'c>означает, что у вас есть объект, который действителен и действителен 'c.

Чего у вас нет, так это определенной связи между этими двумя жизнями. Единственное, что может сделать компилятор, - это то, что, поскольку оно A<'c>находится за a &'b, 'cдолжно пережить 'b, то есть, когда 'bон действителен, так и есть 'c. Хотя, главное, не наоборот.

Как вы показываете, компилятор требует 'bи 'cдолжен быть одинакового времени жизни. Почему это?

Давайте посмотрим на наши возможности:

  1. 'cи не 'bимеют отношения: легко видеть, что без какого-либо отношения компилятор не может ничего гарантировать относительно того, что вставлено, A.borrowи, как таковой, не позволит этого.
  2. 'cстрого отживает 'b, т. е. кое-где 'cдействительно 'bне работает:
    a.borrow = a.borrowстановится повторным заимствованием aиспользования время 'bжизни. Этот ответ объясняет, почему это происходит . Однако это означает, что aтеперь это зависит от времени 'bжизни, которое не действует в течение некоторого времени, aявляется действительным (поскольку aимеет время жизни 'c). Это дает ошибку.
  3. 'bстрого переживает 'c: если бы у нас была эта связь, это могло бы сработать. Повторный заимствования будет действителен, поскольку мы получаем "большее" время жизни ( 'b), чем мы запрашивали ( 'c). Однако мы уже сделали 'c: 'bвыводы компилятора за кулисами. Следовательно, добавление этого времени жизни означает, что два времени жизни становятся равными, и мы возвращаемся к тому, с чего начали:
struct A<'a> {
    borrow: &'a mut u8,
}

/// We add the relation 'b: 'c
fn does_nothing<'b: 'c, 'c>(a: &'b mut A<'c>) {
    a.borrow = a.borrow;
}