Несколько жизней и перемещение: здесь происходит присваивание заимствованному `x`

У меня есть структура с функцией next() (похожей на итераторы, но не на итератор). Этот метод возвращает следующее состояние после модификации (сохранение исходного состояния). Итак: fn next(&A) -> A.

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

Проблема в том, что мне теперь нужно указать время жизни для моей структуры и почему-то мой метод next() больше отказывается работать.

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

Можно ли сохранить поведение моего метода next()?

Попробуйте здесь

#[derive(Clone)]
struct A(u32);
#[derive(Clone)]
struct B<'a>(u32, &'a u32);

impl A {
    fn next(&self) -> A {
        let mut new = self.clone();
        new.0 = new.0 + 1;
        new
    }
}

impl<'a> B<'a> {
    fn next(&self) -> B {
        let mut new = self.clone();
        new.0 = new.0 + 1;
        new
    }
}

fn main() {
    let mut a = A(0);
    for _ in 0..5 {
        a = a.next();
    }

    let x = 0;
    let mut b = B(0, &x);
    for _ in 0..5 {
        b = b.next();
    }
}

Ошибка:

error[E0506]: cannot assign to `b` because it is borrowed
  --> src/main.rs:31:9
   |
31 |         b = b.next();
   |         ^^^^-^^^^^^^
   |         |   |
   |         |   borrow of `b` occurs here
   |         assignment to borrowed `b` occurs here

person Cecile    schedule 02.06.2018    source источник


Ответы (1)


Проблема здесь:

impl<'a> B<'a> {
    fn next(&self) -> B {
        let mut new = self.clone();
        new.0 = new.0 + 1;
        new
    }
}

Вы не указали время жизни для B, возвращаемого типа next. Из-за пожизненных правил исключения в Rust компилятор делает вывод, что вы хотели этого. :

impl<'a> B<'a> {
    fn next<'c>(&'c self) -> B<'c> {
        let mut new = self.clone();
        new.0 = new.0 + 1;
        new
    }
}

Это означает, что возвращаемое значение может не пережить self. Или, другими словами, self должен жить дольше, чем возвращаемый B. Учитывая тело функции, это совершенно ненужное требование, поскольку эти ссылки не зависят друг от друга. И это вызывает проблему здесь:

for _ in 0..5 {
    b = b.next();
}

Вы перезаписываете значение, которое, по мнению средства проверки заимствования, все еще заимствовано при вызове next(). Внутри next мы знаем, что такой связи нет — аннотации времени жизни не отражают ограничений того, что вы на самом деле делаете.

Итак, каковы границы жизни здесь?

  1. Время жизни ссылок на B не связано — одно может существовать без другого. Таким образом, чтобы дать вызывающей стороне максимальную гибкость, время жизни B должно отличаться от времени жизни ссылки на self в next.

  2. Однако каждый B, созданный с помощью next(), содержит ссылку на тот же u32, что и self. Таким образом, параметр времени жизни, который вы указываете для каждого B, должен быть одинаковым.

Используя явно названные времена жизни, это результат объединения обеих этих вещей:

impl<'a> B<'a> {
    fn next<'c>(&'c self) -> B<'a> {
        let mut new = self.clone();
        new.0 = new.0 + 1;
        new
    }
}

Обратите внимание, что хотя ссылка на self здесь имеет время жизни 'c, тип self — это B<'a>, где 'a — это время жизни &u32 внутри. Точно так же, как возвращаемое значение.

Но на самом деле 'c можно опустить. Так что это действительно то же самое, что и это:

impl<'a> B<'a> {
    fn next(&self) -> B<'a> {
        let mut new = self.clone();
        new.0 = new.0 + 1;
        new
    }
}
person Peter Hall    schedule 02.06.2018
comment
Это не только хороший ответ, он помогает мне лучше понять эти воплощения. 100 раз спасибо! - person Cecile; 02.06.2018