Что мне делать, чтобы время жизни в итераторе работало, когда компилятор предлагает использовать ключевое слово move?

У меня есть фрагмент кода, который не компилируется:

struct A {
    x: [u32; 10],
}

impl A {
    fn iter<'a>(&'a self) -> impl Iterator<Item = u32> + 'a {
        (0..10).map(|i| self.x[i])
    }
}

fn main() {}

(детская площадка)

Компилятор говорит:

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
 --> src/main.rs:7:21
  |
7 |         (0..10).map(|i| self.x[i])
  |                     ^^^ ---- `self` is borrowed here
  |                     |
  |                     may outlive borrowed value `self`
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
  |
7 |         (0..10).map(move |i| self.x[i])
  |                     ^^^^^^^^

Что мне делать, чтобы это работало? Мне понадобится self позже, поэтому я не могу переместить его, как предлагает компилятор.

Редактировать: Я полагал, что move создаст проблемы при использовании self позже. Например, посмотрите код:

struct A {
    x: [u32; 3],
}

impl A {
    fn iter<'a>(&'a self) -> impl Iterator<Item=u32> + 'a {
        (0..3).filter(move |&i| self.x[i] != 0).map(move |i| self.x[i])
    }
}

fn main() {
    let a = A { x : [0, 1, 2]};
    for el in a.iter() {
        println!("{}", el);
    }
}

(детская площадка)

Здесь &'a self перемещается дважды, поэтому, по сути, оба закрытия перешли во владение &'a self. Код действительно компилируется, но я не ожидал, что после перемещения переменную больше нельзя будет использовать. В книге (соответствующий раздел) также приводится пример, подтверждающий мое понимание:

fn main() {
    let x = vec![1, 2, 3];
    let equal_to_x = move |z| z == x;
    println!("can't use x here: {:?}", x);
    let y = vec![1, 2, 3];
    assert!(equal_to_x(y));
}

Этот код не компилируется. Почему мой код итератора работает с move?

Просмотрите, как создать минимальный воспроизводимый пример, а затем редактировать свой вопрос, чтобы включить его. На основе код, который вы предоставили существующие ответы решают вашу проблему. Попробуйте создать что-то, что воспроизводит вашу ошибку в Ржавчина Детская площадка, или вы можете воспроизвести это в новом проекте Cargo. Также есть Подсказки MCVE, специфичные для ржавчины.

Shepmaster 23.10.2018 21:43

@Shepmaster Извините, я забыл включить бит, в котором перемещение self в закрытие, как было предложено компилятором, не является вариантом. Мне он понадобится, чтобы использовать его позже, после звонка на iter(). Пожалуйста, дайте мне знать, если что-то еще не завершено.

r.v 23.10.2018 21:46

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

Shepmaster 23.10.2018 21:48

@Shepmaster Я внес некоторые правки в вопрос. Ключевое слово move действительно работает в моем случае, но я не могу понять, почему.

r.v 23.10.2018 23:48

Это потому, что я просто перемещаю ссылку, которая копируется, а не перемещается?

r.v 23.10.2018 23:56

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

Stargateur 24.10.2018 00:45

Вы кардинально изменили свой вопрос, сделав существующие ответы нерелевантными. Это больше не «как мне сделать X», а теперь «почему этот код (который изначально не был опубликован) компилируется». Было бы несправедливо по отношению к людям, которые нашли время ответить на ваш вопрос оригинал, так радикально его изменить. Удалите свои новые вопросы и задайте новый вопрос, касающийся вашего нового вопроса - Как лучше всего задавать дополнительные вопросы? / Стратегии выхода для «вопросов-хамелеонов»

Shepmaster 24.10.2018 01:20
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
7
52
1

Ответы 1

Похоже, компилятор уже говорит вам, что делать:

help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
  |
7 |         (0..10).map(move |i| self.x[i])
  |                     ^^^^^^^^

Ключевое слово move дает замыканию владение любой захваченной переменной.

Здесь вы захватываете ссылку self перемещением, а не сам объект. Вы не можете использовать объект, вызывая метод iter, потому что он принимает ваш объект не по перемещению, а по ссылке. Вы можете по-прежнему использовать свой объект после вызова iter() на экземпляре.

Извините, я почти предположил, что это очевидно ... но мне понадобится self позже. Я отредактировал вопрос сейчас.

r.v 23.10.2018 21:43

@Shepmaster: Хорошо, а какая стандартная фраза здесь? Я имею в виду, что это дает дополнительные ограничения на срок службы 'a из &'a self.

dureuill 23.10.2018 21:48

Это также не накладывает никаких дополнительных ограничений на 'a. Как вы заявляете, он передает право собственности на self (типа &'a Self) закрытию, в отличие от того, чтобы закрытие использовало ссылку на self (типа &&'a Self). Последний не живет достаточно долго, так как выходит за рамки в конце функции.

Shepmaster 23.10.2018 21:55

Хорошо, это проясняет ситуацию. По какой-то причине я думал, что замыкания делают копии захваченных переменных для типов Copy, поэтому причина, по которой пример в вопросе требовал move, на самом деле становилась для меня непонятной. Видимо я не один такой: github.com/rust-lang/rust/issues/36569

dureuill 23.10.2018 22:04

Другие вопросы по теме