Почему тут переменная считается одолженной?

Увидел это какое-то время назад в англоязычном телеграм чате и все никак не могу как следует осознать. Есть такой код:

#[derive(Debug)]
struct X<'a> {
    a: i32,
    b: &'a i32,
}

#[allow(unused)]
fn main() {
    let mut x = X { a: 1, b: &0 };
    let mut y = 2;
    x.b = &y;
    x.b = &0;
    y = 3;
    println!("x: {:?}", x.b);
}

и он не собирается с вот такой ошибкой:

error[E0506]: cannot assign to `y` because it is borrowed
  --> src/main.rs:13:5
11 |     x.b = &y;
   |           -- borrow of `y` occurs here
12 |     x.b = &0;
13 |     y = 3;
   |     ^^^^^ assignment to borrowed `y` occurs here
14 |     println!("x: {:?}", x.b);
   |                         --- borrow later used here

И, вроде, понятно, что это завязанный на структуру 'a виноват и понятно что это меняет время жизни y, но почему именно y считается одолженным в этой ситуации?

Видел похожий вопрос на SO - https://stackoverflow.com/questions/69541461/are-there-any-restrictions-when-using-a-reference-in-a-field-of-a-struct - но тоже как-то не очень помогает глубокое понимание того как реально работает одалживание составить. Может кто-то может пояснить?

Похоже, вариант от @Kolsky лучше всего поясняет как именно оно происходит:

  1. X живёт 'a;
  2. Лайфтайм &0 свободно сокращается до 'a;
  3. Одалживаем y, следовательно, 'a живёт максимум как скоуп функции, и одалживание y становится 'a;
  4. x живёт и валидна как минимум до строчки с println;
  5. 'a продлевается до println;
  6. y, одолженное на 'a, было перезаписано раньше срока одалживания.

Типа можно было бы усложнить анализ NLL для !Drop и сказать, что часть полей всё ещё доступна после окончания лайфтайма, но тут уже семантика слегка изменится

Я не вчитывался, но это не похожее issue?

Надо бы наверное NLL rfc почитать…

1 лайк

Скажите, а что означает &0, или в доку ткните, плс!

0 это строковый литерал, &0 это ссылка на него (с временем жизни 'static)

Андрей, благодарю! С литералами немного непонятно … по сути это строка типа “\0” из C++?
И да, почему static, т.к. задана в явном виде? Или почему?

по сути это строка типа “\0” из C++?

Строковые литералы ("abc") - один из видов литералов, да. Но литералы бывают всякие - 0 целочисленный, 0.0_f32 - флоатовый, () - юнит, true - булевый, MyStruct { my_field: 0 } - структурный. Литерал - просто “буквальная” форма записи некого значения.

почему static, т.к. задана в явном виде?

потому что этот 0 будет жить (в text секции собранного бинаря, например) на протяжении жизни всего приложения, а этот ВЖ называется 'static.

Благодарю! (дополнение до 20 символов в сообщении)