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 而不必使用特征对象。
向量是内存中密集排列的数组,它要求其所有元素占用相同的空间。需要在编译时知道元素的大小。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的大小等于最大的尺寸A和B,可能还要加上空间的判别,指示当前变种是什么。这个大小在编译时是已知的,因此Either可以在向量中使用。
如果A并B实现了一个公共特征,并且您希望能够在向量的元素上调用该特征的方法,您也可以Either通过将所有方法调用转发到正确的变体来实现该特征。
这里的问题是,不能保证仅仅因为两者A和B实现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>。