首页 > 解决方案 > Rust:错误[E0495]:由于闭包中的冲突要求,无法推断自动引用的适当生命周期

问题描述

原始想法

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

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

问题

上面的解决方案不起作用,而且,作为一个 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 。

标签: rustclosureslifetime

解决方案


在您的类型声明中:

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未将返回值分配给变量,因此它们是临时值,并且很快就会被删除。但是即使将它分配给局部变量,也无法返回对它的引用,因为局部变量在函数结束时被删除。

解决方案是返回一个拥有该值的迭代器。像这样的工作:

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()
    })
}

不幸的是,您必须使用 aVec而不是数组,因为没有IntoIterator数组的实现(有切片,但它们不拥有值)。

如果你想避免额外的分配,Vec你可以使用arrayvec允许将迭代器带到数组的 crate:

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,但仍有一些悬而未决的问题。


推荐阅读