你如何写一个返回迭代器的特征?

时间:2015-11-09 03:51:33

标签: rust

从广义上讲,我的目标是:

  • 对于某些已知类型的Bar ...
  • 使用函数get_iterator<T>() -> T where T: Iterator<Item = Bar>
  • 设置特性Foo
  • 迭代器的实例借用原始对象Foo实现。

我想它的工作方式如下:

let mut foo = Foo;
let bar = foo.get_iterator();

foo.mutable_call(); // <-- This fails, because foo is borrowed in bar

for x in bar { 
  ...
}

所以,这就是目标,而这是我的尝试,我似乎无法开始工作:

struct ValsFromT<'a, T: 'a> {
  parent:&'a T,
  offset: usize,
}

struct Val;

trait HasValsIterator<T> {
  fn val_iterator(&self) -> T where T: Iterator<Item = Val>;
}

struct Foo;

impl<'a> Iterator for ValsFromT<'a, Foo> {
  type Item = Val;
  fn next(&mut self) -> Option<Val> {
    return None;
  }
}

impl<'a> HasValsIterator<ValsFromT<'a, Foo>> for Foo {
  fn val_iterator(&'a self) -> ValsFromT<'a, Foo> { 
    return ValsFromT {
      offset: 0usize,
      parent: self
    };
  }
}

fn takes_vals<T>(instance:T) where T: HasValsIterator<T> {
  // ...
}

#[test] 
fn test_foo() {
  let x = Foo;
  takes_vals(x);
}

(游戏围栏:http://is.gd/wys3fx

我们在这里得到了可怕的具体/绑定生命周期错误,因为尝试从trait函数返回引用self的迭代器实例:

<anon>:22:3: 27:4 error: method `val_iterator` has an incompatible type for trait:
 expected bound lifetime parameter ,
    found concrete lifetime [E0053]
<anon>:22   fn val_iterator(&'a self) -> ValsFromT<'a, Foo> { 
<anon>:23     return ValsFromT {
<anon>:24       offset: 0usize,
<anon>:25       parent: self
<anon>:26     };
<anon>:27   }
<anon>:22:3: 27:4 help: see the detailed explanation for E0053

有没有办法做到这一点?

1 个答案:

答案 0 :(得分:5)

不幸的是,Veedrac的建议并不能直接起作用。如果您尝试在val_iterator()内的instance上使用takes_vals()方法,则会收到以下错误:

<anon>:31:25: 31:39 error: the trait `core::iter::Iterator` is not implemented for the type `U` [E0277]
<anon>:31     let iter = instance.val_iterator();
                                  ^~~~~~~~~~~~~~
<anon>:31:25: 31:39 help: see the detailed explanation for E0277
<anon>:31:25: 31:39 note: `U` is not an iterator; maybe try calling `.iter()` or a similar method
error: aborting due to previous error
playpen: application terminated with error code 101

这(以及其他一些其他错误)需要将函数的签名更改为:

fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: T) where T: HasValsIterator<'a, U>

然而,即使这样做还没有成功:

<anon>:31:16: 31:24 error: `instance` does not live long enough
<anon>:31     let iter = instance.val_iterator();
                         ^~~~~~~~
<anon>:30:97: 32:2 note: reference must be valid for the lifetime 'a as defined on the block at 30:96...
<anon>:30 fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: T) where T: HasValsIterator<'a, U> {
<anon>:31     let iter = instance.val_iterator();
<anon>:32 }
<anon>:30:97: 32:2 note: ...but borrowed value is only valid for the scope of parameters for function at 30:96
<anon>:30 fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: T) where T: HasValsIterator<'a, U> {
<anon>:31     let iter = instance.val_iterator();
<anon>:32 }

请注意,该特征要求val_iterator()通过引用接受目标,并使用生命周期'a。此函数中的此生命周期是输入参数。但是,在val_iterator()上调用instance时,可以为参考指定的唯一生命周期是instance,其严格小于任何可能的'a参数,因为它是一个局部变量。因此,无法按值传递instance;你只能通过引用传递它来终生匹配:

fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: &'a T) where T: HasValsIterator<'a, U> {
    let iter = instance.val_iterator();
}

这很有效。

我想补充说,使用关联类型而不是类型参数在语义上会更正确:

trait HasValsIterator<'a> {
    type Iter: Iterator<Item=Val> + 'a;
    fn val_iterator(&'a self) -> Self::Iter;
}

impl<'a> HasValsIterator<'a> for Foo {
    type Iter = ValsFromT<'a, Foo>;
    fn val_iterator(&'a self) -> ValsFromT<'a, Foo> { ... }
}

fn takes_vals<'a, T: 'a>(instance: &'a T) where T: HasValsIterator<'a> {
    ...
}

我说这更正确,因为迭代器的类型由实现者决定,也就是说,它是&#34;输出&#34; type,由关联类型建模。如您所见,takes_vals()签名也大幅缩减。

理想情况下,HasValsIterator特征应该像这样定义:

trait HasValsIterator {
    type Iter<'a>: Iterator<Item=Val> + 'a
    fn val_iterator<'a>(&'a self) -> Iter<'a>;
}

这样,val_iterator()可以在任何情况下,包括HasValsIterator实现者​​按值传递时。但是,Rust还没有。