Suppose you have some objects A
/B
/C
/D
that all implement SomeTrait
, as well as some more objects I
/J
/K
/L
/M
/N
that implement OtherTrait
, and perhaps even more objects X
/Y
/Z
that implement a ThirdTrait
. You can make a struct
that holds a generic of each type:
struct ComposeTraits<T1: SomeTrait, T2: OtherTrait, T3: ThirdTrait> {
first: T1,
second: T2,
third: T3,
}
The goal being that if the ComposeTraits
struct
has functioning versions for each generic it can perform some desired outer/interfacing methods, an InterfaceTrait
, while avoiding internal enum
s or Box<dyn InternalTrait>
s such that everything inside the composed struct works together without indirection/matching.
With this basic setup, you could, if you wanted, match against a tuple of enum
s selecting for each type to be used, and in each match arm return a Box<dyn InterfaceTrait>
. However, since this is composing multiple options, it then requires a number of unique match arms equal to the product of the number variants for each type (4x5x3 = 60 arms for the above setup, yikes!)
However, I've mocked up an idea that extends/reworks the builder pattern to be able to set each generic in turn (i.e. only matching against one enum
per type to set), and still returns the final constructed object into a single, outer Box
.
Here is it working for a simple A1
/A2
+B1
/B2
setup, but it should be sufficient to show that it can be extended to any number of generics with any number of type options: playground
The major goals for this were:
- Create a single runtime-selected and fully-typed
struct
behind just one heap allocation. - Avoiding using
enum
s with extreme size differences for each type. - Maintain internal
impl Trait
arguments so that all compiler optimizations can be applied.
I'm guessing this might be a bit niche, but I'd really appreciate any thoughts on this. Thanks!