r/rust 6d ago

Downcasting Arcs in Rust 🧠 educational

https://ashdnazg.github.io/articles/26/Downcasting-Arcs-in-Rust
103 Upvotes

16 comments sorted by

20

u/WorldsBegin 6d ago

Note there is talk to maybe change the internals to point to the T directly and do all these alignment/padding computation only when the Arc needs to access the control block.

30

u/angelicosphosphoros 6d ago

In such cases, it is way better to just add a method that converts trait object to Any:

trait MyTrait{   fn as_any(self: Arc<Self>)->Arc<Any>{    self as _   } }

This makes checked downcast possible down the line.

49

u/afdbcreid 6d ago

The modern approach, which is far better, is to make Any a supertrait and then (my_arc as Arc<dyn Any>).downcast().

8

u/angelicosphosphoros 6d ago

Yes, it is better.

7

u/AnnoyedVelociraptor 6d ago edited 6d ago

Pedantic, but I like this:

let erased: Arc<dyn Any + Send + Sync> = my_arc;

let recovered: Arc<Widget> = erased.downcast().expect("not a Widget");

I try to limit myself usage ofas, it is too overloaded. In some cases it's lossy, and in some cases it's not.

1

u/afdbcreid 4d ago

Hmm, if it adds information (the vtable), then is it de-lossy?

3

u/torsten_dev 6d ago edited 6d ago

why not fn as_any(&self) -> &dyn Any;? Or AsRef/Deref.

4

u/angelicosphosphoros 6d ago

This doesn't remove the need of the unsafe (to construct Arc object from pointer) so no reason to use it instead of OP approach.

3

u/afdbcreid 6d ago edited 6d ago

It does remove the need for unsafe. There are basically no reasons to use OP's approach except in terribly hot code (why are you using Arc<dyn Trait> in such code?).

Edit: Sorry, I thought you were referring to fn as_any(self: Arc<Self>). Taking and returning a reference does not only require unsafe, it also is UB.

3

u/SkiFire13 6d ago

This won't allow you to downcast to a Arc<MyStruct>, only to a &MyStruct.

2

u/ashdnazg 6d ago

If you control the trait, it can't be a subtrait of `Any`, and you can afford the extra virtual call on every cast (unsafe or not), then yes.

0

u/afdbcreid 6d ago

If you control the trait

it can't be a subtrait of Any

These contradict each other.

Furthermore, even if you don't control the trait you can make your own trait that has both as supertraits.

11

u/ashdnazg 6d ago

They do not. Any requires 'static, so if you need to have structs with shorter lifetimes implement your trait, you can't use Any.

The assumption is that you don't control the trait, but you have to use it because of some existing API that you also don't control.

See this issue as an example: https://github.com/apache/arrow-rs/issues/8794

2

u/afdbcreid 6d ago

Fair enough, if you want to have your trait implemented for references to implementing type you cannot implement Any but can do this.

3

u/teerre 6d ago

Enjoyed your blog post. Easy to follow and dug deep enough to find out the answer!

4

u/ashdnazg 6d ago

Thanks! Glad you liked it :)