Downcasting dyn Any is implemented for plain dyn Any and Box<dyn Any>. But not for Arc<dyn Any> (or other smart pointers).
What is the reasoning here? Would the memory layout be different? As I understand it, the vtable is stored in the fat pointer, which is outside the Arc. So why wouldn't it be possible to downcast?
While I can downcast the inner reference of course, that doesn't let me get an Arc<ConcreteType> back, which complicates my API significantly. What I'm trying to do is a type-erased in-process pub-sub bus, where the topic is the type-id of message. I have code like:
pub trait Message: Any + Send + Sync + 'static {}
impl<T> Message for T where T: Send + Sync + Any + 'static {}
// ...
impl Client {
// This doesn't work, we can't return a reference to to the Arc that goes out of scope...
pub async fn receive<Type: Message>(&mut self) -> Option<&Type> {
if let Some(rx) = self.subscriptions.get_mut(&std::any::TypeId::of::<Type>()) {
let msg = rx.recv().await.ok();
msg.map(|m| {
// Side question: Why do I even need to up-cast to the base trait? Shouldn't downcast_ref exist on &*m?
// Not that it would help, since I need to return a Option<Arc<Type>>...
let any = &*m as &dyn Any;
any.downcast_ref::<Type>()
})
.flatten()
} else {
None
}
}
}
Oh thanks, weird that it is not on the trait, like it is for Box. Perhaps I'll file an issue suggesting adding some cross-links in the docs to make this easier to find.
It is odd still that I have to first upcast to the specific dyn trait it wants.
Eg. given
pub trait Message: Any + Send + Sync + 'static {}
I still need to do:
let a = m as Arc<dyn Any + Send + Sync + 'static>;
a.downcast::<Type>()
Otherwise I get:
error[E0599]: no method named `downcast` found for struct `Arc<dyn Message>` in the current scope
--> src/messaging.rs:140:19
|
140 | m.downcast::<Type>(m)
| ^^^^^^^^ method not found in `Arc<dyn Message>`
|
= note: the method was found for
- `Arc<(dyn Any + Send + Sync + 'static), A>`
I think there is something I don't understand about sub/supertyping and dyn in Rust...
I think that's because Box<_> is fundamental, so if Trait (and thus dyn Trait) is local to your crate, so is Box<dyn Trait>, as far as the orphan rules go. But Arc<_> is not fundamental, so Arc<dyn Trait> is local to Arc's crate, not Trait's crate.
But whatever the reason, cross-links would be good, yeah.
Note that despite the use of terminology like downcast and trait upcasting, there is no actual sub/supertype relationship between dyn Message and dyn Any, or between dyn Trait and concrete types implementing Trait.
The downcast method is an inherent method on Arc<dyn Any + ...>, not a trait method. So Arc<dyn Message> doesn't have that method. Thus the need for some sort of coercion before calling it.
The conversion from Arc<dyn Message> to Arc<dyn Any + ...> is a kind of unsized coercion. And[1] the only automatic unsized coercion performed during method coercion is from arrays to slices. Thus the need for the required coercion to be explicit, not implicit.
It won't automatically work on Arc<ConcreteMessageImplementor> either (which also requires a kind of unsized coercion).
despite a very long-standing inaccuracy in the reference ↩︎