不同类型混凝土的泛型Vec

时间:2018-11-08 21:42:43

标签: rust trait-objects

我有一个特征Foo,具体类型A和B都受该特征Foo限制。我想返回一个Vec<Foo>,其中Foo可以是具体类型A或B,如下所示:

trait Foo { }

pub struct A {}
pub struct B {}

impl Foo for A {}
impl Foo for B {}


fn test() -> Vec<Foo> {
    let generic_vec: Vec<Foo> = Vec::new();
    generic_vec.push(A {});
    generic_vec.push(B {});
    return generic_vec;
}

此刻,编译器抛出了一个错误,即尚未为Foo实现size特质。我可以将Foo包装在一个盒子中,但是由于它们强加了运行时开销,我不想返回Vec的特征对象。

我想知道是否有一些Rust Generics功能可以让我返回通用类型的Vec,而不必使用trait对象。

2 个答案:

答案 0 :(得分:1)

向量是内存中密集排列的数组,它要求其所有元素占用相同的空间量。元素的大小需要在编译时知道。特性对象的大小未知,因此无法将其存储在Vec中。

如果要存储AB的元素向量,则最好的选择是使用枚举:

pub struct A;
pub struct B;

enum Either {
    A(A),
    B(B),
}

fn main() {
    let mut v = vec![];
    v.push(Either::A(A));
    v.push(Either::B(B));
}

枚举Either的大小等于AB的最大值,可能还加了一个空间,用于表示当前变量的判别式。此大小在编译时是已知的,因此Either可以在向量中使用。

如果AB实现了一个共同的特征,并且您希望能够在向量的元素上调用该特征的方法,则可以在Either上实现该特征同样,将所有方法调用转发到正确的变体。

答案 1 :(得分:1)

这里的问题是,不能保证仅因为AB都实现Foo而具有相同的大小。由于Rust的Vec是同质的,因此我们需要静态地保证其中所有元素的大小。

那么,解决方案是对将它们绑定到特征的类型进行装箱。

trait Foo { }

pub struct A {}
pub struct B {}

impl Foo for A {}
impl Foo for B {}

type FooT = Box<dyn Foo>;

fn test() -> Vec<FooT> {
    let mut generic_vec: Vec<FooT> = Vec::new();
    generic_vec.push(Box::new(A {}));
    generic_vec.push(Box::new(B {}));
    return generic_vec;
}

现在类型的大小可能不一样,但是指针(Box)将具有相同的大小,因此尽管需要付出一定的代价,但由于必须取消引用才能访问元素,所以我们完全避免了该问题。

请注意,这里我只是为了便于阅读而定义了类型别名FooT,但是您当然可以只使用Box<dyn Foo>