Struct字段应该都是相同的trait,但不一定是相同的类型



我正试图实现以下特征和结构:

pub trait Funct {
fn arity(&self) -> u32;
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct FunctionLiteral<T: Funct> {
pub function: T,
pub args: Vec< FunctionLiteral<T> >
}
pub enum Foo {
Foo
}
impl Funct for Foo {
fn arity(&self) -> u32 {0}
}
pub enum Bar {
Bar
}
impl Funct for Bar {
fn arity(&self) -> u32 {0}
}
fn main() {
let baz = FunctionLiteral{
function: Foo::Foo,
args: vec![FunctionLiteral{
function: Bar::Bar, 
args: vec![]
}]
};
}

我可以将泛型T设置为性状Funct,但我不一定希望T具有相同的类型。

这里,编译代码会产生以下错误:
error[E0308]: mismatched types
--> foo.rs:31:23
|
31 |             function: Bar::Bar, 
|                       ^^^^^^^^ expected enum `Foo`, found enum `Bar`
error: aborting due to previous error

是否可以设置FunctionLiteral,以便我可以为functionargs的项目设置不同的类型,同时强制它们都是Funct类型?

问题

当你这样做的时候:

Structure<T: Trait>{
inner: T,
many: Vec<T>
}

你告诉编译器为每个不同的T创建一个专门的实例。因此,如果FooBar都实现了Trait,那么编译器将生成两种不同大小的不同表示:

struct Foo(u8);
impl Trait for Foo{
// impl goes here
}
struct Bar(u64);
impl Trait for Bar{
// impl goes here
}

编译器将生成如下内容:

Structure<Foo>{
inner: Foo,
many: Vec<Foo>
}
// and 
Structure<Bar>{  
inner: Bar, 
many: Vec<Bar>
}

显然你不能把Foo实例放到Bar中,因为它们是不同的类型和大小。

的解决方案你需要Box<>你的Funct类型,以使他们相同的大小(即指针大小)。通过将它们放在(智能)指针后面,您实际上是在擦除它们的类型:

let a: Box<dyn Trait> = Box::new(Foo(0));
let b: Box<dyn Trait> = Box::new(Bar(0));

现在ab都有相同的大小(指针的大小)和相同的类型-Box<dyn Trait>。现在你可以输入:

struct Structure{ // no longer generic!
inner: Box<dyn Trait>, // can hold any `Box<dyn Trait>`
many: Vec<Box<dyn Trait>> // can hold any `Box<dyn Trait>`
}

这种方法的缺点是它需要堆分配,并且会丢失ab的确切类型。你不再知道aFoo还是Bar或其他东西。

代替Box,你可以使用任何其他智能指针,如RcArc如果你需要它的功能。

另一个选择另一个选择是使FooBar的大小和类型相同。这可以通过将它们包装在enum:

中来完成。
enum Holder{
Foo(Foo),
Bar(Bar),  // any other types go here in their own variants
}

然后你的结构看起来像:

struct Structure{ // no longer generic!
inner: Holder, // can hold any Holder variant`
many: Vec<Holder> // can hold any Holder variant`
}

缺点是你必须实现像

这样的委托:
impl Trait for Holder{
fn some_method(&self){
match self{
Holder::Foo(foo) => foo.some_method(),
Holder::Bar(bar) => bar.some_method(),
}
}
}

match在您想要使用对象的任何地方。现在你的Holderenum的大小将是max(sizeof(Foo), sizeof(Bar))

正面:

  • 你仍然知道实际类型-它没有被擦除
  • 没有堆分配

相关内容

最新更新