I was wondering if it is possible to cast a trait object to another trait object.
I tried the following code:
trait TraitA {
fn say_hello(&self) {
self.say_hello_from_a();
}
fn say_hello_from_a(&self);
}
trait TraitB {
fn say_hello(&self) {
self.say_hello_from_b();
}
fn say_hello_from_b(&self);
}
struct MyType {}
impl TraitA for MyType {
fn say_hello_from_a(&self) {
println!("Hello from A");
}
}
impl TraitB for MyType {
fn say_hello_from_b(&self) {
println!("Hello from B");
}
}
fn main() {
let a: Box<dyn TraitA> = Box::new(MyType{});
let b: Box<dyn TraitB>;
a.say_hello();
b = a;
b.say_hello();
}
I get the following compilation error:
error[E0308]: mismatched types
--> trait_objects.rs:34:9
|
34 | b = a;
| ^ expected trait `TraitB`, found trait `TraitA`
|
= note: expected type `std::boxed::Box<dyn TraitB>`
found type `std::boxed::Box<dyn TraitA>`
So what I tried to do was to declare 2 traits and a type called MyType. Then I implemented both traits for MyType. I created a new trait object TraitA of type MyType which I called a. But since a also implements TraitB, I thought it should be able to be casted as TraitB.
Haven't figured out if it's even possible. If it is, how can I cast trait object a into TraitB.
Note: In C++, I would normally use something similar to std::dynamic_pointer_cast<TraitB>(a);
for the same purpose.
Thanks
EDIT: I'm adding an example of a case where I could use lateral casting.
Let's say I have a struct with some data inside that represents some real life entity:
struct MyType {
let a: i32;
let b: i32;
}
Instances of this type can be used in at least two different parts of the code base. On both parts I need a behavior called let's say get_final_value
.
The interesting part is that get_final_value should respond different depending on who called it.
Why don't I split the type into 2 different ones?: Technically, by design,
a
andb
belong together, not to say thatget_final_value()
uses both values to compute the result.Why not use generics/static dispatch? Because MyType is just one example. In the real case I have different
structs
, all of them implementing both traits in different ways.Why not use
Any
trait? To be honest, I didn't know of it's existence until recently. I don't recall The Rust Programming Language book mentioning it. Anyway, it seems you need to know the concrete type to do a cast from Any to that concrete type and then to the trait object.
Another option might be to create a trait that uses both TraitA and TraitB as supertraits and provides a cast to each type:
Then have MyType implement it:
Once you do that, you simply use TraitC for your Box and your program logic that uses both TraitA and TraitB together.
Sample main to show various ways to use:
If A and B do truly belong together, this better represents that and still gives you the freedom to use them separately if you desire.
Link to the Rust Playground
Using
Box<MyType>
instead ofBox<dyn Trait>
solves this problem.In this case there's no need to use trait objects. Rust has a different paradigm from C++. In most cases you can usually use generic types to solve problems. If your problem is really suitable to solve with trait objects, you can refer to the OOP chapter in the book.