结构字段应该都是相同的特征,但不一定是相同的类型
Posted
技术标签:
【中文标题】结构字段应该都是相同的特征,但不一定是相同的类型【英文标题】:Struct fields should be all of same trait, but not neceesarily same type 【发布时间】:2021-11-06 05:49:30 【问题描述】:我正在尝试实现以下特征和结构:
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
以便我可以为function
和args
的项目设置不同的类型,同时强制它们都为Funct
类型?
【问题讨论】:
是的,你应该Box
他们
Box<dyn Funct>
特别是。那么FunctionLiteral
甚至不需要是通用的。见doc.rust-lang.org/book/ch17-02-trait-objects.html
【参考方案1】:
问题
当你这样做时:
Structure<T: Trait>
inner: T,
many: Vec<T>
您告诉编译器为每个不同的T
创建一个专用实例。所以如果你有Foo
和Bar
都实现了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));
现在a
和b
具有相同的大小(指针的大小)并且具有相同的类型 - 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>`
这种方法的缺点是它需要堆分配并且它丢失了a
和b
的确切类型。你不再知道a
是Foo
还是Bar
还是别的什么。
如果您需要它的功能,您可以使用任何其他智能指针代替Box
,例如Rc
或Arc
。
另一种选择
另一种选择是使Foo
和Bar
具有相同的大小和类型。这可以通过将它们包装在 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
在您想使用该对象的任何地方。现在你的Holder
枚举的大小也将是max(sizeof(Foo), sizeof(Bar))
有利的一面:
你仍然知道实际的类型 - 它没有被删除 没有堆分配【讨论】:
以上是关于结构字段应该都是相同的特征,但不一定是相同的类型的主要内容,如果未能解决你的问题,请参考以下文章