Hey, so I have this setup in my code where I use three traits to generalize a variety of different types which have wildly different APIs and which all perform one of three same underlying functions.To show you what it looks I made this example where I simplified these 3 traits to show the logic so I just put some random stuff for the functions but the return types, arguments and associated types are very similar to my code.
It looks something like this:
pub trait TraitA: Clone {
type B: TraitB;
type C: TraitC;
fn test_one(&self) -> Self::B;
fn test_two(&self) -> u32;
fn test_three(b: Self::B, c: Self::C) -> Self;
}
impl TraitA for u64 {
type B = u16;
type C = u8;
fn test_one(&self) -> Self::B { 1 }
fn test_two(&self) -> u32 { 1 }
fn test_three(t: Self::B, s: Self::C) -> Self { 1 }
}
//#########################################
pub trait TraitB {
type A: TraitA;
type C: TraitC;
fn test_one(&self) -> Self::A;
fn test_two(&self) -> u32;
fn test_three(b: Self::C) -> Result<Self, ()> where Self: Sized;
}
impl TraitB for u16 {
type A = u64;
type C = u8;
fn test_one(&self) -> Self::A { 2 }
fn test_two(&self) -> u32 { 2 }
fn test_three(b: Self::C) -> Result<Self, ()> { Err(()) }
}
//#########################################
pub trait TraitC {
fn test(&self) -> u32;
}
impl TraitC for u8 {
fn test(&self) -> u32 { 3 }
}
//#########################################
//this function passes the compiler
fn function_using_traits<A: TraitA, B: TraitB>
(a: A, b: B, c: Box<dyn TraitC>)
{ println!("just checking if using these args works"); }
//this one obviously doesn't given TraitA and TraitB are not 'object safe'
fn other_function_using_traits
(a: Box<dyn TraitA>, b: Box<dyn TraitB>, c: Box<dyn TraitC>)
{ println!("just checking if using these args works"); }
This code compiles fine (except for other_function_using_traits()) and its the most efficient solution I have found for the problem I have. I am not interested in changing how it's set up and I'm not really in a position to do so.
Now the problem is that sometimes when I call on these traits in other bits of my code, I do not know what type to call on, I just know the type has implemented one of these traits. I want to be able to handle these without having to know their types, just the fact that they implement certain traits. I want to be able to have dynamic dispatch essentially. The easy answer then is to just use Box<dyn Trait> which I would like to do except TraitB has Self is Sized and TraitA has Clone as a supertrait. So after looking around I found out that I can just erase the traits by making another trait which is object safe and using that. The problem is I don't know how to do that for TraitA and TraitB given my code is very different from any example I found online.
What I found online so far helped me understand how it works but I am struggling to apply it to my situation. I'm sort of hoping someone can guide me through how to do this because for now I'm just stumbling in the dark and fighting the compiler.
I looked at these threads/articles: