`DispatchFromDyn` for `&Rc<Self>` and `&Arc<Self>`

I like the pattern of an interface trait that reference counts self, like so:

trait MyTrait {
    fn clone_one_or_create(self: &Arc<Self>, other: &Arc<dyn MyTrait>) -> Arc<dyn MyTrait>;
}

With support only for "single indirections" like Arc<Self>, the closest pattern now is cloning the object before every method call, a.clone().foo(), which is bad for readability and makes a lot of needless atomic increment/decrement calls.

I found the guidance that dynamic dispatch is impossible behind multiple indirections confusing, but accepted that it makes sense if dynamic dispatch from Arc<dyn Trait> involves constructing something like a temporary Arc<T>.

Now that I know about std::raw::TraitObject layout and DispatchFromDyn, though, it makes less sense, because all current implementations of DispatchFromDyn don't create at temporary, the compiler just drops the metadata field and relabels the data as *mut ArcInner<()>, or similar. This means that to dispatch from &Arc<dyn Trait>, the compiler can just relabel the argument to *mut Arc<()>: there isn't even any pointer arithmetic, because the data is the first field in the trait object.

@kennytm knew this more than two years ago: I wish I knew the right search terms to find this thread a while ago!

Is implementation of DispatchFromDyn for &Box<T>, &Rc<T>, and &Arc<T> already being planned? I think it would be really useful.

1 Like

I agree with the original point of the linked thread, that we should have Arc0<'_, T>/Rc0<'_, T> as "+0" reference counted pointers. They're obviously not perfect, as it's implemented in userland and thus I can't implement some traits, but I have userland implementations of "+0" refcounted pointers in my crate,

With a second voice of support, I'd be happy to work to get these upstreamed into alloc and available for everyone. They don't require any new support, just mainly a whole bunch of trait impls.

This would solve the double indirection dispatch question by removing the need for the double indirection.

Your pointer types could be nice to work with, but the motivation for this thread is the dispatch problem for trait objects, which I don't think an &Arc with borrow semantics can solve.

Were they std types, they could implement DispatchFromDyn directly, the same way that A/Rc do, which sidesteps the double indirection problem.

The double indirection issue would remain, but be less relevant.

This isn't quite correct in my understanding. I have &dyn Trait, you have &T. These are distinct in memory via a move/copy. There's no extra temporary involved, but a new pointer is still copied into the new stack frame.

Allowing &&dyn Trait to dispatch to &&T would require stabilizing the fact that dyn trait pointers are stored data pointer first. This is explicitly not guaranteed yet, and is a big ask just to support double indirection dyn dispatch, when using a single indirection is likely better code and is fairly simple to allow.

(When double indirection is needed is when one of the indirections is not shared+immutable. And in that case, dyn dispatch would be unsound, as you would be able to replace a subslice of the pointed at object. Or IOW, &mut &dyn Trait can never be DispatchFromDyn, even if &&dyn Trait theoretically could.)

Yes, what I'm asking for is probably deeply tied up in #![feature(ptr_metadata)], I'm just requesting that in the process of those changes that DispatchToDyn be implemented for the types we're discussing.

Whether that takes the form of &Arc or ArcBorrow, though, the changes would have been be fairly equivalent, right? Logic like dyn_dispatch_rc_ref from the original thread would have to added to the compiler either way.

Okay, sorry, now I see how ArcBorrow works. Yes, if that were object safe then that would also work.