Rust: ошибка [E0495]: невозможно определить подходящее время жизни для автоматического восстановления в соответствии с конфликтующими требованиями при закрытии - PullRequest
1 голос
/ 02 февраля 2020

Необработанная идея

В моем фиктивном проекте я хотел бы использовать циклические итераторы (например, для генерации целых чисел).

use std::iter::Cycle;

type IntegerCycle = Cycle<std::slice::Iter<'static, i32>>;

fn generate_cycles() -> [IntegerCycle; 2] {
  let mut cycles = [
    [1, 2].iter().cycle(),
    [2, 4].iter().cycle(),
  ];

  cycles
}


fn main() {
  let mut cycles = generate_cycles();
  // ...
}

Refactor

Хотя предыдущий фрагмент кода работает как задумано, мой пример из реального мира немного сложнее, поэтому я хочу адаптировать функцию generate_cycles, чтобы она могла выполнять больше операций (в следующем примере умножьте на 2, затем сгенерируйте циклические итераторы). Для этого я попытался использовать arraymap :

extern crate arraymap;

use arraymap::ArrayMap;
use std::iter::Cycle;

type IntegerCycle = Cycle<std::slice::Iter<'static, i32>>;

fn generate_cycles() -> [IntegerCycle; 2] {
  let mut cycles = [
    [1, 2],
    [2, 4],
  ];

  cycles
    .map(|points| {
      points.map(|point| point*2)
    })
    .map(|points| {
      points.iter().cycle()
    })
}


fn main() {
  let mut cycles = generate_cycles();
  // ...
}

Проблема

Приведенное выше решение не работает и, как новичок Rust, недавно подвергшийся воздействию концепции «жизни», я не понимаю, почему компилятор жалуется здесь, или что я могу сделать, чтобы сделать его счастливым.

error[E0495]: cannot infer an appropriate lifetime for autorefdue to conflicting requirements
  --> src/main.rs:20:14
   |
20 |       points.iter().cycle()
   |              ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 19:10...
  --> src/main.rs:19:10
   |
19 |       .map(|points| {
   |  __________^
20 | |       points.iter().cycle()
21 | |     })
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:20:7
   |
20 |       points.iter().cycle()
   |       ^^^^^^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected [std::iter::Cycle<std::slice::Iter<'static, i32>>; 2]
              found [std::iter::Cycle<std::slice::Iter<'_, i32>>; 2]

Вот REPL с кодом, пытающимся использовать arraymap: https://repl.it/repls/ShadowyStrikingFirm.

1 Ответ

1 голос
/ 03 февраля 2020

В вашем объявлении типа:

type IntegerCycle = Cycle<std::slice::Iter<'static, i32>>;

Вы говорите, что базовые фрагменты, которые вы используете для построения своих итераторов, должны иметь 'static время жизни, то есть они должны жить вечно. Затем вы используете литеральные массивы, такие как [1, 2], которые, как и все литералы, имеют 'static' время жизни и все идет хорошо:

let r: &'static [i32; 2] = &[1, 2]; //Ok

Но затем вы пытаетесь код, подобный этому более простому:

let a = [1, 2].map(|x| 2 * x);
let r: &'static [i32; 2] = &a; //error: borrowed value does not live long enough

В результате arraymap::map является обычным массивом, а не литералом, поэтому у него нет времени жизни 'static. Это не может быть stati c, потому что вы вычисляете значения во время выполнения. Он будет жить столько времени, сколько необходимо, в моем случае до тех пор, пока переменная a.

В вашем случае, поскольку возвращаемые значения arraymap::map не присваиваются переменным, они являются временными значениями и являются быстро упал. Но даже если вы присвоили его локальной переменной, вы не могли бы вернуть ссылку на нее, потому что локальная переменная удаляется после завершения функции.

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

type IntegerCycle = Cycle<std::vec::IntoIter<i32>>;

fn generate_cycles() -> [IntegerCycle; 2] {
  let cycles = [
    [1, 2],
    [2, 4],
  ];
  cycles
    .map(|points| {
      points.map(|point| point*2)
    })
    .map(|points| {
      points.to_vec().into_iter().cycle()
    })
}

К сожалению, вам нужно использовать Vec вместо массива, потому что нет реализации IntoIterator для массивов (есть для слайсов, но они не владеть значениями).

Если вы хотите избежать дополнительного выделения Vec, вы можете использовать ящик arrayvec, который позволяет переносить итератор в массив:

type IntegerCycle = Cycle<arrayvec::IntoIter<[i32; 2]>>;

fn generate_cycles() -> [IntegerCycle; 2] {
  let cycles = [
    [1, 2],
    [2, 4],
  ];

  cycles
    .map(|points| {
      points.map(|point| point*2)
    })
    .map(|points| {
        let a = arrayvec::ArrayVec::from(*points);
        a.into_iter().cycle()
    })
}

ПРИМЕЧАНИЕ: Похоже, что есть попытка добавить правильный IntoIterator impl для массивов со значением к std, но все еще есть некоторые нерешенные проблемы.

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