This is an idea I've been thinking about for some time now and I thought I'd share it. This is my first post here, so I hope I'm doing it right.
Idea
In short, the idea would be to have traits which impose a fixed size on its implementors. For example, by having a special `Fixed` trait. Consequences of this would be: default Sized trait objects, no need for indirection, matching on trait objects,...Traits and Enums
I got this idea by looking into subtype/inclusion polymorphism in rust. The way I see it, there are two main ways to do "subtyping" (some might not like this term, but I think fits here).The first is traits with trait objects as the polymorphic types and the implementors as subtypes. Example:
trait MyTrait {
fn method(&self) -> String;
}
struct Variant1;
struct Variant2;
impl MyTrait for Variant1 {
fn method(&self) -> String {/*impl*/}
}
impl MyTrait for Variant2 {
fn method(&self) -> String {/*impl*/}
}
The second is enums with instances of this enum as polymorphic types and its variants as subtypes. Example:
enum MyEnum {
Variant1,
Variant2
}
impl MyEnum {
fn method(&self) -> String {
match self {
Variant1 => /*impl*/,
Variant2 => /*impl*/
}
}
}
Both methods have advantages and disadvantages, but there is always a kind of "cost" (not necessarily a runtime cost). For traits, the main cost is the need for indirection, while for enums the main cost is that all variants have the same size (including padding), but there are different costs too (see comparison).
Fixed Sized Traits
Fixed sized traits are an attempt to combine both traits and enums to have a different "cost" option. I see these fixed sized traits in two possible ways: using a vtable or using a tag.The first makes them very similar to normal traits. The only difference is the sized property. This changes the trait indirection cost into the enum fixed size cost. Example:
trait MyFixedTrait : Fixed<4u> { //arbitrary size
fn method(&self) -> String;
}
struct Variant1;
struct Variant2;
impl MyFixedTrait for Variant1 {
fn method(&self) -> String {/*impl*/}
}
impl MyFixedTrait for Variant2 {
fn method(&self) -> String {/*impl*/}
}
The second option would be to use a tag instead. This makes them similar to enums, so I'll call them enum traits. For a tag to work, the user would need to specify what variants are possible. So a new (type) declaration would be needed that specifies the fixed trait and the variants. Example:
enum trait MyEnumTrait for MyFixedTrait {
Variant1,
Variant2
}
Comparison
Trait | Fixed sized trait | Enum trait | Enum | |
---|---|---|---|---|
Sized | No | Yes | Yes | Yes |
Extend with new variants | Yes | Yes | No | No |
Use variants separately | Yes | Yes | Yes | No |
Automatic polymorphism* | Yes | Yes | Yes | No |
Pattern matching | No | No | Yes | Yes |
Variant differ | vtable | vtable | tag | tag |