【问题标题】:How do I return a reversed iterator?如何返回反向迭代器?
【发布时间】:2021-06-22 23:42:13
【问题描述】:

我正在编写一些我想使用迭代器的代码,或者它的反转版本取决于一个标志,但是简单的代码给出了一个错误

pub fn eggs<I,T>(iter:I)->Box<dyn Iterator<Item=T>>
where I:Iterator<Item=T>+DoubleEndedIterator
{
    Box::new(iter.rev())
}

pub fn bacon<I,T>(iter:I, reverse:bool) -> Box<dyn Iterator<Item=T>>
    where I:Iterator<Item=T>+DoubleEndedIterator
{
    if reverse {
        Box::new(iter.rev())
    } else {
        Box::new(iter)
    }

}

fn main()
{
    let pants:String = "pants".into();
    eggs(pants.chars());
}

编译失败:

error[E0310]: the parameter type `I` may not live long enough
 --> src/main.rs:5:5
  |
2 | pub fn eggs<I,T>(iter:I)->Box<dyn Iterator<Item=T>>
  |             - help: consider adding an explicit lifetime bound...: `I: 'static`
...
5 |     Box::new(iter.rev())
  |     ^^^^^^^^^^^^^^^^^^^^ ...so that the type `Rev<I>` will meet its required lifetime bounds

由于我对 Rust 的了解有限,我不确定这些生命周期界限的来源。 Iterator trait 或 Rev 结构上没有任何参数,并且正在移动参数。

鉴于'static 并不是一个真正的选择,声明这些函数的正确方法是什么。

rust playground

【问题讨论】:

  • 您可能还想查看 either 板条箱而不是装箱。

标签: rust


【解决方案1】:

这根本与.rev() 无关,而是与返回Box&lt;dyn Iterator&gt;

// error[E0310]: the parameter type `I` may not live long enough
fn boxed_iter<I, T>(iter: I) -> Box<dyn Iterator<Item = T>>
//            - help: consider adding an explicit lifetime bound...: `I: 'static`
where
    I: Iterator<Item = T>,
{
    Box::new(iter)
//  ^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds
}

这样做的原因是,如果没有指定,像 Box&lt;dyn Trait&gt; 这样的 trait 对象具有隐式的 'static 生命周期。因此,当编译器尝试将Box&lt;I&gt; 强制转换为Box&lt;dyn Iterator&gt; 时,如果I 也没有'static 生命周期,它就会失败。 (如果特征本身包含生命周期,则有一些更具体的规则;您可以阅读更多详细信息here。)

如果您想要更短的生命周期,则需要将其明确指定为Box&lt;dyn 'a + Trait&gt;。比如:

fn boxed_iter<'a, I, T>(iter: I) -> Box<dyn 'a + Iterator<Item = T>>
where
    I: 'a + Iterator<Item = T>,
{
    Box::new(iter)
}

【讨论】:

    【解决方案2】:

    Frxstrem 的回答非常好。我只是想补充一点,如果您知道函数的返回值具有特定的具体类型,则可以使用特殊的impl trait syntax

    对于您的eggs 函数,返回类型可能类似于Rev&lt;I&gt;。这种类型本身并不是很有启发性,但我们知道这种类型存在,我们唯一关心的是它是一个迭代器,所以我们可以编写

    pub fn eggs<I,T>(iter:I) -> impl Iterator<Item=T> + DoubleEndedIterator
    where I: Iterator<Item=T> + DoubleEndedIterator {
      iter.rev()
    }
    

    现在编译器仍然知道有一个具体的类型,并会采取相应的行动(不需要装箱或动态分派),但我们作为程序员仍然只需要关心Iterator 和@987654326 @它的各个方面。最佳的零成本抽象。

    您的bacon 函数无法从中受益,因为它可能会根据输入返回IRev&lt;I&gt;,因此动态调度实际上是必要的。在这种情况下,您需要按照 Frxstrem 的回答正确装箱您的迭代器。

    【讨论】:

      猜你喜欢
      • 2017-06-13
      • 2010-10-27
      • 1970-01-01
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多