结构字段应该都具有相同的特征,但不一定是相同的类型
Struct fields should be all of same trait, but not neceesarily same type
我正在尝试实现以下特征和结构:
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
?
问题
当你这样做时:
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))
好的方面:
- 您仍然知道实际类型 - 它没有被删除
- 没有堆分配
我正在尝试实现以下特征和结构:
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
?
问题
当你这样做时:
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))
好的方面:
- 您仍然知道实际类型 - 它没有被删除
- 没有堆分配