While working on my Rust project, I encountered the need to both store objects that implement the same trait in one collection, and the need to return one of several different iterators from one function, depending on some conditions.
All these problems are solved trivially using Box<dyn Trait>
. However, such a dispatch has its drawbacks, the main one being the need to use vtable, which has a very bad effect on inlining and can significantly slow down the program compared to using static dispatch.
Unfortunately, at the moment static dispatch is achieved by explicitly creating enum
types, either with pasting boilerplate while impl
necessary traits, or by using procedural macros that always have limited flexibilty (i.e. the enum_dispatch
crate).
However, this approach cannot be considered universal, since the explicit creation of such enum
s is difficult for iterator types due to the complexity of their type denotation, and generally impossible for non-denotable closure types.
By creating this topic, I propose to think about adding anonymous trait objects with static dispatch.
Possible notation
// As a parameter
fn foo(_: static Trait) {
/* Body omitted */
}
// As a return type
fn bar() -> static Trait {
/* Body omitted */
}
// As a template parameter
fn baz() {
let vec = Vec::<static Trait>::new();
}
Examples
The following code snippets show the possible examples of their notation and their use.
Example 1
struct A { /* fields omitted */ }
struct B { /* fields omitted */ }
trait Trait { /* body omitted */ }
impl Trait for A { /* impls omitted */ }
impl Trait for B { /* impls omitted */ }
fn main() {
let mut vec = Vec::<static Trait>::new();
vec.push(A::new());
vec.push(B::new())
}
Example 2
As of today, the following code won't compile without Box<dyn Iterator<Item=u64>>
.
fn different_iterators(cond: bool) -> static Iterator<Item=u64> {
if cond {
[2].into_iter()
} else {
[3, 4].into_iter()
}
}
Possible compiler representation
As a possible representation of statically dispatched trait objects, I suggest using an enum
automatically generated by the compiler, each variant of which can store one of the concrete types that are defined from the context of use of the static Trait
. This enum
should automatically derive the Trait
trait.
Pros
Because of inlining, calling the Trait
methods are usually faster when using match
to unpack such an enum
, as opposed to using a vtable.
Cons
Such an enum
could consume much more memory than dynamically dispatched trait object, since its size is the sum of the size of the largest type from the context and additional bytes that store information about the concrete type stored in it.