0

Is there any way to cast a trait object (dyn A) to another type (dyn B) safely?

trait A {}
trait B: A {}
impl <T: A> B for T {}

fn cast(x: &dyn A) -> &dyn B {
  x
}
error[E0308]: mismatched types
 --> src/lib.rs:6:3
  |
6 |   x
  |   ^ expected trait `B`, found trait `A`
  |
  = note: expected type `&dyn B`
             found type `&dyn A`
Jiaming Lu
  • 875
  • 6
  • 20
  • Since trait A doesn't necessarily implement type A, no. You need to have `trait B: A {}` – jhpratt Sep 29 '19 at 06:58
  • I have tried with B: A, not working. – Jiaming Lu Sep 29 '19 at 07:04
  • It would still need an explicit cast. Why do you need to do this, though? Most of the time static dispatch is sufficient. – jhpratt Sep 29 '19 at 07:05
  • And `trait B: A` means, any B is also A, but not vice versa. – Jiaming Lu Sep 29 '19 at 07:05
  • I just want to have some helper functions in trait B. Surely I can have other solutions, just wondering why this kind of cast not working – Jiaming Lu Sep 29 '19 at 07:06
  • It's not working because it's not safe. – Zefick Sep 29 '19 at 07:12
  • And I want to know why is it not safe, since for every , B is implemented. – Jiaming Lu Sep 29 '19 at 07:18
  • It's not just that it's not safe in general. When you construct a trait object from a value, it includes a vtable pointer for that trait depending on the value's concrete type. That vtable only includes methods from that one trait. Casting to a subtrait or unrelated trait would imply working out the concrete type from the vtable, then somehow finding the right vtable for the other trait. To my knowledge, it's not possible to implement casts like this directly; the information just isn't available at runtime. See the linked duplicate for workarounds. – Francis Gagné Sep 29 '19 at 07:34
  • @FrancisGagné Now I understand why it's not working, but for the vtable part, since I have the vtable of A, why can't I build a vtable of B based on the vtable of A, it's just another layer of indirection. – Jiaming Lu Sep 29 '19 at 07:43
  • The vtable of `A` doesn't contain a vtable for `B`, or any information that would allow you to recover one. If you reverse the supertrait relationship (`A: B`), you could add an `as_b` method to the trait ([example](https://stackoverflow.com/questions/28632968/why-doesnt-rust-support-trait-object-upcasting)), or you could add an `as_any` method and attempt to downcast to some concrete type that you know implements `B` ([see](https://stackoverflow.com/questions/27892375/can-i-do-type-introspection-with-trait-objects-and-then-downcast-it)). – trent Sep 29 '19 at 11:53
  • @trentcl, I means, from `impl B for T`, all methods in (this implementation of) B must call only methods contained in the vtable of A. Although the vtable of B is not contained in the vtable of A, it can be resolved in compile time. Of cause now I see rust not support this. – Jiaming Lu Sep 30 '19 at 03:34
  • It *cannot* be resolved at compile time, because by using `dyn` you explicitly defer the knowledge of *which* concrete type you're dealing with to runtime. `cast` does not have access to compile time type information, because it is not generic; it only knows what you give it at runtime. – trent Sep 30 '19 at 09:07

0 Answers0