Proposal
Problem statement
Currently, we have downcast methods for Box<dyn Any>, Box<dyn Any + Send>, Box<dyn Any + Send + Sync>, Rc<dyn Any>, and Arc<dyn Any + Send + Sync>, but we are missing it for Arc<dyn Any>.
Note that it is perfectly reasonable to store non-Send + Sync data inside an Arc, as it still gives one shared type for generic storage, and later downcasting/dynamic dispatch can recover the Send + Sync bound as needed.
Motivating examples or use cases
Real-World code-patterns of where Arc::<dyn Any>::downcast would have been useful:
- Manual implementations of the method
- Users cloning the data through
downcast_ref instead of the surrounding Arc<dyn Any>
- Users only exposing the result of
downcast_ref
- Example 10, Example 11
- Call sites of APIs like this often end up cloning the data too, leading back to the previous code pattern
Note that the latter two patterns (cloning the data from behind downcast_ref) lead to code using Arc<dyn Any> to ensure the type-erased data can be cloned around, but then failing to retrieve a long-lived version of the data cheaply. This partially defeats the purpose of using Arc in the first place.
From personal experience, I have seen (and written) code that avoids the "retrieval clone" by wrapping the data itself inside another Arc, thus leading to unnecessary double indirection and code complexity. This may even be unavoidable if the type-erased data is something that can't/shouldn't be cloned, such as a Mutex.
Solution sketch
I propose a new downcasting method for Arc<dyn Any> named downcast_local. See the "Alternatives" section for why it might not be possible for this method to be named downcast.
impl<A: Allocator> Arc<dyn Any, A> {
pub fn downcast_local<T: Any>(self) -> Result<Arc<T>, Self> { ... }
}
Alternatives
One alternative is not doing anything; thus leaving users to write inefficient and overcomplicated code, or to implement the functionality themselves.
We may also consider using the traditional downcast name, instead of the proposed downcast_local, to mimic the way that the Box::downcast methods all share a name.
Note however that since Arc::<dyn Any + Send + Sync>::downcast is currently the only method with that name, adding Arc::<dyn Any>::downcast would add a possible coercion target, thus breaking the following code:
trait MySubtrait: std::any::Any {}
fn downcast_my_subtrait(x: Arc<dyn MySubtrait + Send + Sync>) -> Option<Arc<i32>> {
// `x` implicitly coerces to `Arc<dyn Any + Send + Sync>`.
// This would error with "multiple applicable items in scope" if we added `Arc::<dyn Any>::downcast`,
// as can be seen in analogous code with `Box::downcast`!
Arc::downcast(x).ok()
}
I.e. upcasting coercions from Arc<dyn SubtraitOfAnySendSync> to Arc<dyn Any + Send + Sync> in argument position would break when calling downcast using associated function syntax. The impact would have to be assessed early, as I believe the breakage would be insta-observable in stable code, even if added as #[unstable].
The advantages of this would be better consistency and a clearer path towards adding methods like Arc::<dyn Error (+ ...)>::downcast, which would otherwise require yet another name.
Links and related work
- Related discussion on zulip.
- PR that introduced
Arc::downcast
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Proposal
Problem statement
Currently, we have
downcastmethods forBox<dyn Any>,Box<dyn Any + Send>,Box<dyn Any + Send + Sync>,Rc<dyn Any>, andArc<dyn Any + Send + Sync>, but we are missing it forArc<dyn Any>.Note that it is perfectly reasonable to store non-
Send + Syncdata inside anArc, as it still gives one shared type for generic storage, and later downcasting/dynamic dispatch can recover theSend + Syncbound as needed.Motivating examples or use cases
Real-World code-patterns of where
Arc::<dyn Any>::downcastwould have been useful:downcast_refinstead of the surroundingArc<dyn Any>downcast_refNote that the latter two patterns (cloning the data from behind
downcast_ref) lead to code usingArc<dyn Any>to ensure the type-erased data can be cloned around, but then failing to retrieve a long-lived version of the data cheaply. This partially defeats the purpose of usingArcin the first place.From personal experience, I have seen (and written) code that avoids the "retrieval clone" by wrapping the data itself inside another
Arc, thus leading to unnecessary double indirection and code complexity. This may even be unavoidable if the type-erased data is something that can't/shouldn't be cloned, such as aMutex.Solution sketch
I propose a new downcasting method for
Arc<dyn Any>nameddowncast_local. See the "Alternatives" section for why it might not be possible for this method to be nameddowncast.Alternatives
One alternative is not doing anything; thus leaving users to write inefficient and overcomplicated code, or to implement the functionality themselves.
We may also consider using the traditional
downcastname, instead of the proposeddowncast_local, to mimic the way that theBox::downcastmethods all share a name.Note however that since
Arc::<dyn Any + Send + Sync>::downcastis currently the only method with that name, addingArc::<dyn Any>::downcastwould add a possible coercion target, thus breaking the following code:I.e. upcasting coercions from
Arc<dyn SubtraitOfAnySendSync>toArc<dyn Any + Send + Sync>in argument position would break when callingdowncastusing associated function syntax. The impact would have to be assessed early, as I believe the breakage would be insta-observable in stable code, even if added as#[unstable].The advantages of this would be better consistency and a clearer path towards adding methods like
Arc::<dyn Error (+ ...)>::downcast, which would otherwise require yet another name.Links and related work
Arc::downcastWhat happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution: