3

我有一个特质 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;
}

此刻的编译器正在抛出未为 Foo 实现大小特征的错误。我可以将 Foo 包装在一个 Box 中,但我不想返回一个 Vec 特征对象,因为它们会带来运行时开销。

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

4

2 回答 2

9

向量是内存中的一个密集排列的数组,它要求它的所有元素占用相同的空间。在编译时需要知道元素的大小。特征对象没有已知的大小,因此您不能将它们存储在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通过将所有方法调用转发到正确的变体来实现该特征。

于 2018-11-08T21:50:41.473 回答
4

这里的问题是不能保证仅仅因为两者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;
}

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

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

于 2018-11-08T21:53:04.977 回答