rust-lang / rust-lang/libs-team
ACP: Add a downcasting method for `Arc<dyn Any>`
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 178
- Forks
- 28
- Avg merge
- 15m
- Merged PRs (30d)
- 1
Description
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_refinstead of the surroundingArc<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.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the proposal's solution sketch and alternatives for Arc::downcast_local, then read the linked feature lifecycle and related Zulip discussion. Compare the requested API with the existing Arc::downcast behavior and assess the compatibility concern around coercions. Done means the libs-api team has reviewed and decided whether the API and naming should proceed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100