用于存储具有不同通用参数的结构的特征

Trait to store structs with different generic parameters

我需要存储相同结构的相同 Vec 个实例,但具有不同的通用参数。这是结构定义:

struct Struct<'a, T: 'a> {
    items: Vec<&'a T>
}

该结构有一个方法将迭代器返回到不依赖于泛型类型参数的类型 T:

impl<'a, T: 'a> Struct<'a, T> {
    fn iter(&self) -> slice::Iter<&i32> {
        unimplemented!()
    }
}

我需要为向量中的那些不同结构访问这个方法,所以我实现了这个特性:

type Iter<'a> = Iterator<Item=&'a i32>;

trait Trait {
    fn iter(&self) -> Box<Iter>;
}

而且我已经实现了 Struct 的特性:

impl<'a, T: 'a> Trait for Struct<'a, T> {
    fn iter(&self) -> Box<Iter> {
        Box::new(self.iter())
    }
}

但是编译器抱怨:

<anon>:21:9: 21:30 error: type mismatch resolving `<core::slice::Iter<'_, &i32> as core::iter::Iterator>::Item == &i32`:
expected &-ptr,
    found i32 [E0271]
<anon>:21         Box::new(self.iter())
                  ^~~~~~~~~~~~~~~~~~~~~
<anon>:21:9: 21:30 help: see the detailed explanation for E0271
<anon>:21:9: 21:30 note: required for the cast to the object type `core::iter::Iterator<Item=&i32> + 'static`
<anon>:21         Box::new(self.iter())
                  ^~~~~~~~~~~~~~~~~~~~~

我已经尝试了特征中生命周期参数的不同可能性,但是 none 它们都有效。我怎样才能使这项工作?

Rust Playground snippet

编辑

正如@MatthieuM 指出的那样。一个问题是类型别名不能正常工作。这是另一个证明这一点的例子:

use std::slice;

type Iter<'a> = Iterator<Item=&'a i32>;

struct Struct<'a> { _phantom: std::marker::PhantomData<&'a i32> }

impl<'a> Struct<'a> {
    fn direct<'b>(i: &'b slice::Iter<'a, i32>) -> &'b Iterator<Item=&'a i32>
    { i }

    fn aliased<'b>(i: &'b slice::Iter<'a, i32>) -> &'b Iter<'a>
    { i }
}

在此示例中,direct 编译,但 aliased 未编译,错误为:

<anon>:12:7: 12:8 error: the type `core::slice::Iter<'a, i32>` does not fulfill the required lifetime
<anon>:12     { i }
                ^
note: type must outlive the static lifetime

但它们似乎是一回事。发生什么事了?

问题 1 — slice::Iter<T>Iterator::Item&T,因此您的参考水平不匹配。将您的方法更改为

fn iter(&self) -> slice::Iter<i32>

问题 2 — Box<SomeTrait> 等同于 Box<SomeTrait + 'static>,但您的迭代器不会在 'static 生命周期内存在。你需要明确的带入一个生命周期:

Box<SomeTrait + 'a>

问题 3 — 我不明白如何为特征创建类型别名,这看起来很奇怪。无论如何你可能都不想要它。相反,为整个盒装版本创建一个类型别名:

type IterBox<'a> = Box<Iterator<Item=&'a i32> + 'a>;

问题 4 — 重新排列您的 main 以便引用能够存在足够长的时间并增加可变性:

fn main() {
    let i = 3;
    let v = vec![&i];
    let mut traits : Vec<Box<Trait>> = Vec::new();
    traits.push(Box::new(Struct{ items: v }));
}

总计:

use std::slice;

type IterBox<'a> = Box<Iterator<Item=&'a i32> + 'a>;

trait Trait {
    fn iter<'a>(&'a self) -> IterBox;
}

struct Struct<'a, T: 'a> {
    items: Vec<&'a T>
}

impl<'a, T: 'a> Struct<'a, T> {
    fn iter(&self) -> slice::Iter<i32> {
        unimplemented!()
    }
}

impl<'a, T: 'a> Trait for Struct<'a, T> {
    fn iter(&self) -> IterBox {
        Box::new(self.iter())
    }
}

fn main() {
    let i = 3;
    let v = vec![&i];
    let mut traits: Vec<Box<Trait>> = Vec::new();
    traits.push(Box::new(Struct { items: v }));
}