Because the typeid is useless to get the actual type, i.e. you can't go from a typeid to a type. It is a hash, so if two types are equal, then their typeids are also equal, but not the other way around. Any uses the fact that the hash is good to go from typeid to type, not really. It still needs you to tell it which type is inside. For example,
fn get(x: &dyn Any) -> &??? {
magic_on_any(x)
}
How would this function's signature be written? What is the return type? This function is the one you proposed that would use some information inside the trait object to convert the trait object to a concrete type.
There is a reason why you can only as Any is this the type inside you via Any::is::<T>(), but to ask what type is inside.
This is possible to implement, but the Rust team has deliberately not implemented it because there are some important edge cases to handle. For example, imagine this
/// In crate Foo
trait Foo {
fn foo(&self) {}
fn qaz(&self) {}
}
/// In crate Bar,
trait Bar: Foo {
fn bar(&self) {}
fn hal(&self) {}
}
struct MyType;
impl Foo for MyType {}
impl Bar for MyType {}
The layout of a trait object is currently
struct TraitObject {
ptr: *const (),
vtable: *const ()
}
Now, Foo and Bar's vtable looks something like
struct FooVtable {
qaz: fn(*const()),
foo: fn(*const()),
...
}
struct BarVtable {
hal: fn(*const()),,
qaz: fn(*const())
bar: fn(*const()),
foo: fn(*const()),
...
}
I have snipped out the unnecessary details. Now we need a way to convert from BarVTable to FooVtable. In this case, we could just copy over the qaz and foo functions and be done.
But what if we want to support multi-trait objects? (dyn Foo + Bar + Any + Debug + ...). Then we would want to be able to convert to any sub-set of trait objects right? (dyn Foo + Any + Debug). If so, we have a combinatorial explosion of required conversions, but this is very bad for dylibs because they must provide every combination of traits that they can, which is unsustainable. The other way to solve this problem would be to make pointers to trait objects super-fat, i.e.
struct TraitObject {
ptr: *const (),
vtables: [*const (); N]
}
where N is the number of super traits or traits in the multi-trait object. But this has a huge penalty to micro controllers and other devices that are memory constrained.
So as you can see the problem is quite a bit more complicated than at first glance.