to the gc crate, in order to allow object-safety of a method taking self: Gc<Self>, as is currently the case for self: Rc<Self>. But that doesn’t compile:
error[E0277]: the trait bound `Cell<NonNull<GcBox<T>>>: DispatchFromDyn<Cell<NonNull<GcBox<U>>>>` is not satisfied
--> gc/src/lib.rs:56:1
|
56 | impl<T: Trace + ?Sized + Unsize<U>, U: Trace + ?Sized> DispatchFromDyn<Gc<U>> for Gc<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `DispatchFromDyn<Cell<NonNull<GcBox<U>>>>` is not implemented for `Cell<NonNull<GcBox<T>>>`
To fix this, would it be reasonable to add a DispatchFromDyn implementation for UnsafeCell and Cell?
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<UnsafeCell<U>> for UnsafeCell<T> {}
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Cell<U>> for Cell<T> {}
Yes, it’s gated behind #![feature(dispatch_from_dyn)]. Obviously if gc were to use this, the use would be conditional on the non-default nightly crate feature along with its existing use of #![feature(coerce_unsized, unsize)], or perhaps some other even-more-nightly crate feature. I understand the caveats of using unstable nightly features—that isn’t my question. The reason it’s unstable in the first place is we might want to change it, and I’m proposing a way we might want to change it, so my question is whether this change makes sense.
That’s the same placeholder text with which the book describes 48 such features. I understand that DispatchFromDyn is not stable and has not been proposed for stabilization. Again, my question is not whether using DispatchFromDyn is a good idea. My question is specifically about adding a DispatchFromDyn implementation for Cell.
To separate my question from the particular unstable mechanism that would be used to implement it: should a trait with a method taking self: Cell<Self>self: Cell<SmartPtr<Self>> (using the arbitrary_self_types feature, which does have a tracking issue) be object-safe?
The answer to that, I would assume, is no. Cell<Self> is !Sized for Self: !Sized, which is distinct from all other DispatchFromDyn. Thus, it's just as impossible to call a Cell<Self> method on a trait object as it would be to call a Self method.
If you are, however, referring to Cell<SmartPtr<Self>>, that may be slightly more reasonable. However, it would require at least two things, and the UnsafeCell version would not be possible to do soundly (as it cannot read out of an UnsafeCell that might have a &mut to it's contents):
SmartPtr would have to be Copy (to be able to safely read out of the Cell)
SmartPtr<Self> would have to be DispatchFromDyn itself.
Edit: I don't know why I thought that, it's Cell<T> not &Cell<T>... It's safe to convert an UnsafeCell, or Cell into it's value.
I'm not sure how useful this would be outside of this case, however.
Dyn dispatch only works on an indirection to a trait object.
Cell<Self> passes Self by value, so cannot be an object safe receiver, because that's a contradiction. (You would then be passing Cell<dyn Trait> as Self.)
&[mut] Cell<dyn Trait> could at least in theory perform dyn dispatch, but that's not what you're asking about.
NonNull<T> can't be an object safe receiver (currently) either, because it's currently undecided if a dangling pointer still is required to have a valid vtable.
If Gc<T> is always guaranteed to at some point have pointed to some valid T, you probably want (the moral equivalent of) Cell<&'unsafe UnsafeCell<T>>. (Note that the outer Cell has no impact on the mutability/aliasing guarantees of the pointee, just the pointer itself!) This doesn't currently exist in the language, but would guarantee all the guarantees provided by a reference, but with a user-enforced lifetime. The difference from NonNull (excepting variance) being guarantees of a) being aligned, b) valid pointer metadata describing a place that existed at some point, and c) accesses enforcing standard reference aliasing rules (so use UnsafeCell).
Er yes, Cell<SmartPtr<Self>> is what I meant. (Gc<T> is essentially Cell<NonNull<GcBox<T>>>, where GcBox<T> is a struct with T and some header information. It actually uses a bit of the pointer as a bit to indicate whether the Gc is rooted, so there isn’t an alignment guarantee. But we know that the vtable is valid because at some point the Gc originated from a Gc::new call.)