我有一个特征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对象。
答案 0 :(得分:1)
向量是内存中密集排列的数组,它要求其所有元素占用相同的空间量。元素的大小需要在编译时知道。特性对象的大小未知,因此无法将其存储在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));
}
枚举Either
的大小等于A
和B
的最大值,可能还加了一个空间,用于表示当前变量的判别式。此大小在编译时是已知的,因此Either
可以在向量中使用。
如果A
和B
实现了一个共同的特征,并且您希望能够在向量的元素上调用该特征的方法,则可以在Either
上实现该特征同样,将所有方法调用转发到正确的变体。
答案 1 :(得分:1)
这里的问题是,不能保证仅因为A
和B
都实现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>
。