【问题标题】:How do you write a trait that returns an iterator?你如何编写一个返回迭代器的特征?
【发布时间】:2015-11-09 03:51:33
【问题描述】:

总的来说,我的目标是:

  • 对于一些已知类型的 Bar...
  • 有一个特征 Foo 和一个函数:get_iterator<T>() -> T where T: Iterator<Item = Bar>
  • 迭代器的实例借用了实现 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

有没有办法做到这一点?

【问题讨论】:

  • @Veedrac 哦,这只是语义,实际上并不是借用 self 的问题。如果你想把它变成答案,我会接受的~

标签: rust


【解决方案1】:

很遗憾,Veedrac 的建议并不能直接奏效。如果您尝试在instance 内部takes_vals() 上使用val_iterator() 方法,您将收到以下错误:

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

请记住,该 trait 要求 val_iterator() 通过引用接受目标,其生命周期为 'a。此函数中的此生命周期是一个输入参数。但是,当在instance 上调用val_iterator() 时,可以为引用指定的唯一生命周期是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> {
    ...
}

我说这样比较正确,因为迭代器的类型是由实现者决定的,也就是“输出”类型,它是由关联类型建模的。如您所见,takes_vals() 签名也大幅缩水。

理想情况下,HasValsIterator trait 应该这样定义:

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

这样,val_iterator() 在任何情况下都会发生,包括当HasValsIterator 实现者按值传递时。但是,Rust 还没有出现。

【讨论】:

    猜你喜欢
    • 2016-02-16
    • 2013-11-22
    • 1970-01-01
    • 2010-09-23
    • 2020-07-07
    相关资源
    最近更新 更多