2014-07-04 24 views
11

我有一个一生问题,我想实现一个迭代器参考返回它的项目,这里是代码:迭代器返回的参考项目,寿命问题

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 

是否有人有一个想法如何解决这个问题,仍然通过引用返回的项目?

至少这个信息是什么意思:预期的具体生命期,但发现绑定的生命期参数

回答

14

Note on the version of Rust used: at the time this question and answer were written, the Iterator trait used generics; it has changed to use associated types and is now defined thus:

pub trait Iterator { 
    type Item; 

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

And so the incorrect implementation shown here would be like this:

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

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

In practical terms this affects nothing; it is merely that A becomes Self::Item .

Iterator特征的定义是这样的:

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

注意小心:fn next(&mut self) -> Option<A>

这里是你拥有的一切:仔细

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

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

这里有几个问题:

  1. 你介绍了一个新的通用参数<'a>它不应该存在。为方便起见并强调此处发生的事情,我将复制在方法ρ1上定义的impl块ρo和'a上定义的'a他们不一样。

  2. &mut self的生命期与特征不同。

  3. 返回类型的寿命是该性状不同:其中A&'ρ₀ u8,返回类型中的A&'ρ₁ u8的地方使用。它预期的具体寿命ρ0,但发现寿命ρ1。 (我不能确定正是的“约束”位意味着什么,所以我将它保持安静,免得我错了。)

下面是这相当于:您无法连接的寿命您正在迭代到&mut self的对象。相反,它必须被绑定到你正在实现特征的类型中。举一个例子,迭代切片中的项目是通过创建一个连接到基础切片impl<'a, T> Iterator<&'a T> for Items<'a, T>的新迭代器对象完成的。用另一种方式表达,迭代特征的设计方式不是,如果您正在生成引用,则返回self中的某些内容,而是返回您有参考的另一个对象内的内容。

对于您特定的,可能很简单的示例,您应该停止生成引用或更改引用,以便您的迭代器对象不包含正在迭代的数据 - 让它只包含对其的参考。&'a [T]甚至像Items<'a, T>

+0

非常有帮助的回答。我必须说1-为了理解和正确使用struct和traits中指定的生命/泛型类型/特征类型以及方法中使用的类型/特征类型,我正在苦苦挣扎,因此我在这个例子中使用了“无处不在”。 2 - 我认为我理解你的观点很重要,我尝试着grep Rust代码及其库,以便找出类似的情况是如何处理的,以及你所说的似乎对应于在结构中使用生命周期参数的几种情况带有一个被引用的属性,例如libcore/slice.rs中的Splits迭代器。感谢您的帮助 – user3762625

+0

有时您可能会发现标准库中的示例难以理解;例如,'Items'实际上是用宏生成的东西,所以您需要先理解Rust的宏的基本知识,然后才能开始使用它!随时准备下降irc://irc.mozilla.org/#rust,那里总会有人来帮忙。 –

+0

我会注意到,有一个重要的原因是返回一个其生命周期不是*链接到迭代器的项目:这允许在迭代中实现*转换*,这在C++中是很难做到的。 –