rust-lang / rust-lang/libs-team

ACP: Add a downcasting method for `Arc<dyn Any>`

Open
#862 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

api-change-proposal
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:

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.