I'm having some trouble understanding the bounds of object safety: why is self: Arc<Self>
an object safe receiver, but self: &Arc<Self>
is not?
Because Arc<Self>
has one indirection where &Arc<Self>
has two. This means that you can trivially cast from Arc<Concrete>
to Arc<Dynamic>
but you can't cast from &Arc<Concrete>
to &Arc<Dynamic>
Okay, I understand that if struct S
implements trait T
that I can cast Arc<S>
to Arc<dyn T>
but not &Arc<S>
to &Arc<dyn T>
.
But the conversion that has to happen in dynamic dispatch goes the other way: if a
is an Arc<dyn T>
, then for a.foo()
to dispatch to an impl with the signature fn foo(self: Arc<S>)
requires a cast from Arc<dyn T>
to Arc<S>
.
So what you say is correct, but I don't think it's an explanation.
The same reasoning applies. We can't construct a &Arc<Concrete>
from a &Arc<Dynamic>
. (for example, std::mem::size_of::<Arc<Concrete>>() != std::mem::size_of::<Arc<Dynamic>>()
).
i.e. please implement this function, (I use [i32]
here for example, but the same reasoning applies to trait objects)
// you may assume that the slice has the proper length.
unsafe fn cast(arc_ref: &Arc<[i32]>) -> &Arc<[i32; 10]> {
todo!()
}
You may use any sort of unsafe wizardry that you desire, but the returned reference must be valid, and you can't rely on the layout of Arc
being [ptr, len]
.
Here is an example implementation for Arc<[i32]>
,
// you may assume that the slice has the proper length.
unsafe fn cast(arc: Arc<[i32]>) -> Arc<[i32; 10]> {
Arc::from_raw(Arc::into_raw(arc) as *const [i32; 10])
}
We can simplify this further to just references if that makes it easier to implement.
// you may assume that the slice has the proper length.
unsafe fn cast<'a>(arc_ref: &&'a [i32]) -> &'a [i32; 10] {
todo!()
}
hint hint: you can't implement the cast function when you have double indirection.
Thanks for taking the time to explain it.
It seems strange at first that if I have a: &Arc<dyn T>
, that I have to do a.clone().foo()
---make a clone that lasts the duration of the function call. I was thinking that an implementation of composed receivers could implement a: &Arc<Self>
by essentially making that clone behind the scenes on the stack and passing a reference to it. I finally realize that this would not be a valid dispatch because the lifetime of the &Arc<S>
would be different from the lifetime of the &Arc<dyn T>
.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.