迭代器通过引用返回项目,生命周期问题

Posted

技术标签:

【中文标题】迭代器通过引用返回项目,生命周期问题【英文标题】:Iterator returning items by reference, lifetime issue 【发布时间】:2014-08-25 19:39:47 【问题描述】:

我有一个 lifetime 问题,我正在尝试实现一个通过引用返回其项目的迭代器,代码如下:

struct Foo 
   d: [u8; 42],
   pos: usize


impl<'a> Iterator<&'a u8> for Foo 
   fn next<'a>(&'a mut self) -> Option<&'a u8> 
      let r = self.d.get(self.pos);
      if r.is_some() 
         self.pos += 1;
      
      r
   


fn main() 
   let mut x = Foo 
      d: [1; 42],
      pos: 0
   ;

   for i in x 
      println!("", i);
   

但是这段代码没有正确编译,我得到一个与参数生命周期有关的问题,这里是相应的错误:

$ rustc test.rs
test.rs:8:5: 14:6 error: method `next` has an incompatible type for trait: expected concrete lifetime, but found bound lifetime parameter
test.rs:8     fn next<'a>(&'a mut self) -> Option<&'a u8> 
test.rs:9         let r = self.d.get(self.pos);
test.rs:10         if r.is_some() 
test.rs:11             self.pos += 1;
test.rs:12         
test.rs:13         r
           ...
test.rs:8:49: 14:6 note: expected concrete lifetime is the lifetime 'a as defined on the block at 8:48
test.rs:8     fn next<'a>(&'a mut self) -> Option<&'a u8> 
test.rs:9         let r = self.d.get(self.pos);
test.rs:10         if r.is_some() 
test.rs:11             self.pos += 1;
test.rs:12         
test.rs:13         r
           ...
error: aborting due to previous error

有人知道如何解决这个问题并仍然通过引用返回项目吗?

至少这条消息意味着什么:预期的具体生命周期,但找到绑定的生命周期参数

【问题讨论】:

【参考方案1】:

注意使用的 Rust 版本: 在撰写本问答时,Iterator trait 使用了泛型;它已更改为使用关联类型,现在定义如下:

pub trait Iterator 
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    …

所以这里显示的不正确的实现是这样的:

impl<'a> Iterator for Foo 
    type Item = &'a u8;

    fn next<'a>(&'a mut self) -> Option<&'a u8>;

实际上,这不会产生任何影响;只不过@9​​87654325@变成了Self::Item

Iterator trait 的定义如下:

pub trait Iterator<A> 
    fn next(&mut self) -> Option<A>;
    …

注意:fn next(&amp;mut self) -&gt; Option&lt;A&gt;

这是你所拥有的:

impl<'a> Iterator<&'a u8> for Foo 
    fn next<'a>(&'a mut self) -> Option<&'a u8>;

注意:fn next&lt;'a&gt;(&amp;'a mut self) -&gt; Option&lt;&amp;'a u8&gt;

这里有几个问题:

    您引入了一个不应存在的新通用参数&lt;'a&gt;。为了方便和强调这里发生的事情,我将在 impl 块 ρ₀ 上定义的 'a 和在方法 ρ₁ 上定义的 'a 命名。 它们不一样。

    &amp;mut self 的生命周期与 trait 的生命周期不同。

    返回类型的生命周期与特征不同:其中A&amp;'ρ₀ u8,返回类型使用A &amp;'ρ₁ u8 的位置。它期望具体的寿命 ρ₀,但发现的却是寿命 ρ₁。 (我不确定准确“绑定”位是什么意思,所以我会保持沉默,以免我错了。)

这相当于:你不能将你正在迭代的对象的生命周期连接到&amp;mut self。相反,它必须绑定到您正在为其实现特征的类型中的某些东西。举个例子,通过创建一个连接到基本切片impl&lt;'a, T&gt; Iterator&lt;&amp;'a T&gt; for Items&lt;'a, T&gt; 的新迭代器对象来对切片中的项目进行迭代。以另一种方式表达,迭代特征的设计方式不是,如果您正在生成引用,则返回 self 内的内容,而是返回您有引用的另一个对象内的内容。

对于您的具体的、可能很简单的示例,您应该停止产生引用,或者更改它以使您的迭代器对象不包含您正在迭代的数据——让它只包含一个 引用对它,例如&amp;'a [T] 甚至像 Items&lt;'a, T&gt; 这样的东西。

【讨论】:

非常有帮助的答案。我必须说1-我在理解和正确使用结构和特征中指定的生命周期/通用类型/特征类型以及方法中使用的类型方面付出了很多努力,因此我在这个例子中到处都使用了'a。 2-我想我很理解你的观点,我试图对 Rust 代码及其库进行 grep 以查找类似情况的处理方式,并且你所说的似乎对应于在结构中使用生命周期参数的几种情况具有引用属性,例如 libcore/slice.rs 中的拆分迭代器。感谢您的帮助 有时您可能会发现标准库中的示例难以理解;例如,Items 实际上是用宏生成的,所以你需要了解 Rust 宏的基础知识才能理解它!随时准备好访问 irc://irc.mozilla.org/#rust,总会有人提供帮助。 我会注意到,返回一个生命周期链接到迭代器的项目有一个重要原因:这允许在迭代,例如在 C++ 中很难做到这一点。 这个答案不再有效。显然RFC447 2015 年合并打破了它,我现在收到错误 impl&lt;'a&gt; Iterator for Foo // unconstrained lifetime parameter @JonasBerlin:这个答案从来没有包含功能代码,它只是解释了为什么尝试的东西不起作用。

以上是关于迭代器通过引用返回项目,生命周期问题的主要内容,如果未能解决你的问题,请参考以下文章

临时对象的生命周期:嵌套函数调用中临时向量的迭代器

爬入结构类型的迭代器的通用生命周期参数

使用refs实现迭代器时的生命周期推断问题

无法为返回引用的闭包推断适当的生命周期

JVM类加载器及Java类的生命周期

PMBook 中的开发生命周期