Недоумение заимствует сообщение проверки - PullRequest
5 голосов
/ 27 января 2020

Я недавно столкнулся с сообщением о проверке заимствований, которое раньше никогда не видел, и которое я пытаюсь понять. Вот код для его воспроизведения (упрощенный, реальный пример был более сложным) - детская площадка :

fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
    let dest = if which { &mut v1 } else { &mut v2 };
    dest.push(1);
}

Не удается скомпилировать со следующей ошибкой:

error[E0623]: lifetime mismatch
 --> src/main.rs:2:44
  |
1 | fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
  |            ------------      ------------ these two types are declared with different lifetimes...
2 |     let dest = if which { &mut v1 } else { &mut v2 };
  |                                            ^^^^^^^ ...but data from `v2` flows into `v1` here

... а затем еще один о данных, поступающих из v1 в v2.

Мой вопрос: что означает эта ошибка? Что такое поток данных и как он происходит между двумя переменными, учитывая, что код передает только Copy данные одной из них?

Если я следую за компилятором и форсирую время жизни v1 и Для соответствия v2 функция компилирует ( площадка ):

fn foo<'a>(mut v1: &'a mut Vec<u8>, mut v2: &'a mut Vec<u8>, which: bool) {
    let dest = if which { &mut v1 } else { &mut v2 };
    dest.push(1);
}

Однако при дальнейшей проверке выяснилось, что исходный код был излишне сложным, оставшимся после того, как v1 и v2 были фактическими Vec с, а не ссылками. Более простой и естественный вариант - установить dest не на &mut v1 и &mut v2, а на более простые v1 и v2, которые являются ссылками для начала. И это тоже компилируется ( детская площадка ):

fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
    let dest = if which { v1 } else { v2 };
    dest.push(1);
}

В этой, казалось бы, эквивалентной формулировке время жизни совпадений v1 и v2 больше не требуется.

Ответы [ 2 ]

4 голосов
/ 27 января 2020

Проблема в том, что &'a mut T является инвариантом над T.

Сначала давайте посмотрим на рабочий код:

fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
    let dest = if which { v1 } else { v2 };
    dest.push(1);
}

Типы v1 и v2 имеют исключенные параметры времени жизни. Давайте сделаем их явными:

fn foo<'a, 'b>(v1: &'a mut Vec<u8>, v2: &'b mut Vec<u8>, which: bool) {
    let dest = if which { v1 } else { v2 };
    dest.push(1);
}

Компилятор должен определить тип dest. Две ветви выражения if создают значения разных типов: &'a mut Vec<u8> и &'b mut Vec<u8>. Несмотря на это, компилятор может определить тип, который совместим с обоими типами; назовем этот тип &'c mut Vec<u8>, где 'a: 'c, 'b: 'c. &'c mut Vec<u8> это общий супертип как &'a mut Vec<u8>, так и &'b mut Vec<u8>, поскольку оба 'a и 'b переживают 'c (т. Е. 'c - это более короткий / меньший срок службы, чем любой 'a или 'b).

Теперь давайте рассмотрим ошибочный код:

fn foo<'a, 'b>(v1: &'a mut Vec<u8>, v2: &'b mut Vec<u8>, which: bool) {
    let dest = if which { &mut v1 } else { &mut v2 };
    dest.push(1);
}

Опять же, компилятор должен определить тип dest. Две ветви выражения if производят значения типов: &'c mut &'a mut Vec<u8> и &'d mut &'b mut Vec<u8> соответственно (где 'c и 'd - это время жизни sh).

Ранее я говорил, что &'a mut T инвариантно над T. Это означает, что мы не можем изменить T в &'a mut T так, чтобы мы могли создать подтип или супертип &'a mut T. Здесь типами T являются &'a mut Vec<u8> и &'b mut Vec<u8>. Они не одного типа, поэтому мы должны сделать вывод, что типы &'c mut &'a mut Vec<u8> и &'d mut &'b mut Vec<u8> не связаны между собой. Следовательно, для dest.

нет действительного типа.
1 голос
/ 27 января 2020

У вас есть вариант этой ошибочной программы:

fn foo(x: &mut Vec<&u32>, y: &u32) {
  x.push(y);
}

Раньше сообщения об ошибках были немного более расплывчаты, но были изменены с помощью этого запроса на получение . Это случай дисперсии, о котором вы можете прочитать больше в nomicon , если вам интересно. Это сложная тема, но я постараюсь объяснить все как можно быстрее.

Если вы не укажете время жизни, когда вы возвращаете &mut v1 или &mut v2 из вашего оператора if, типы определяются компилятором как разные времена жизни, таким образом возвращая другой тип. Поэтому компилятор не может определить правильное время жизни (или тип) для dest. Когда вы явно устанавливаете все времена жизни одинаковыми, компилятор теперь понимает, что обе ветви оператора if возвращают одинаковое время жизни, и он может выяснить тип dest.

В приведенном выше примере x имеет другое время жизни от y и, следовательно, другой тип.

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