Rust:错误 [E0495]:由于闭包中的需求冲突,无法推断出自动引用的适当生命周期

Rust: error[E0495]: cannot infer an appropriate lifetime for autorefdue to conflicting requirements in closure

原始想法

在我的虚拟项目中,我想使用循环迭代器(例如生成整数)。

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();
  // ...
}

重构

虽然前面的代码按预期工作,但我的真实世界示例有点复杂,因此我希望调整 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();
  // ...
}

问题

上述解决方案不起作用,而且,作为最近接触"lifetime"概念的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,其中的代码试图利用 arraymaphttps://repl.it/repls/ShadowyStrikingFirm .

在你的类型声明中:

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 生命周期。它不能是静态的,因为您是在运行时计算值。只要有必要,它就会存在,在我的例子中,只要变量 a.

在您的情况下,由于 arraymap::map 的 return 未分配给变量,因此它们是临时值,很快就会被删除。但是即使你把它赋值给一个局部变量,你也不能 return 引用它,因为局部变量在函数结束时被删除。

解决方案是 return 拥有该值的迭代器。像这样的东西有效:

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 for arrays by value 添加到 std,但仍有一些悬而未决的问题。