不同具体类型泛型的 Vec

And*_*ham 2 rust trait-objects

我有一个特征 Foo,具体类型 A 和 B 都受特征 Foo 的限制。我想返回 a 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;
}
Run Code Online (Sandbox Code Playgroud)

目前的编译器抛出错误,即未为 Foo 实现 sized trait。我可以将 Foo 包装在一个 Box 中,但我不想返回 trait 对象的 Vec,因为它们会带来运行时开销。

我想知道是否有一些 Rust 泛型特性可以让我返回泛型类型的 Vec 而不必使用特征对象。

Sve*_*ach 8

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

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

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));
}
Run Code Online (Sandbox Code Playgroud)

枚举Either的大小等于最大的尺寸AB,可能还要加上空间的判别,指示当前变种是什么。这个大小在编译时是已知的,因此Either可以在向量中使用。

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


Ber*_*rer 5

这里的问题是,不能保证仅仅因为两者AB实现Foo它们将具有相同的大小。由于 RustVec是同构的,我们需要静态地保证其中所有元素的大小。

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

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;
}
Run Code Online (Sandbox Code Playgroud)

现在,类型可能不具有相同的大小,但指针(Box)具有相同的大小,因此我们完全避免了这个问题,尽管要付出一些代价,因为我们必须取消引用才能访问元素。

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