rust-lang / rust-lang/libs-team

ACP: Remove 'static from Any itself, moving the bound to the operations themselves

Open
#819 4 comments 4 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

The Any trait has a 'static bound. This is unnecessarily restrictive. It means you can't add Any as a supertrait to your own traits if you wish to support downcasting, unless you already have a 'static bound yourself.

The trait would be more useful if we push this bound to the relevant operations/functions, rather than the trait itself.

Motivating examples or use cases

The immediate motivator for this ACP is Error. There has been an effort to add type_id to dyn Error (https://github.com/rust-lang/rust/issues/60784). If you look at that issue, and at a large amount of the implementations on Error, we are essentially just re-implementing Any on Error. This is because we can't simply add an Any bound to Error because of the 'static bound, as there might be Error types which contain lifetimes. This problem applies to any trait which is intended to be used in dyn while also being intended for things containing lifetimes.

If we move the 'static bound to the relevant methods/associated functions instead of the trait itself, we solve the above problem and can simply add Any as a supertrait to Error. Then Error + 'static automatically gets type_id. In general with this change one can freely add Any as a supertrait to support downcasting.

Future use cases (not proposed here)

But, should we take this first step, we also unlock the capability for future enhancements for the language in general:

  1. If we remove the 'static bound, everything and anything implements Any. This means it might be possible in the future to make Any an 'auto-supertrait', always implied by any dyn Trait. This means you can always downcast from dyn Trait, even if no one bothered to specify Any explicitly. The cost of this is slightly enlarging every single dyn vtable with the information needed to downcast. C++ makes this choice, for example.

  2. We can possibly relax the 'static bound away from TypeId::of entirely in the future. If so then you can request the type id of any dyn Trait where Trait : Any (and if we do the previous suggestion that means, always). This can be quite nice for debugging. Note that this relaxation only applies to querying a type id, not to downcasting, and that there is a previously-closed RFC to remove this bound: https://github.com/rust-lang/rfcs/blob/master/text/1849-non-static-type-id.md.

  3. Safe downcasting from dyn Any + 'a, not just dyn Any + 'static. For some types you can soundly downcast to it from dyn Any + 'a for any 'a, as the TypeId matching lets us conclude there is no lifetime issue. This includes types such as u32 and struct Foo { name: &'static str } but not &'static str itself (as it's indistinguishable from &'a str) - the exact requirements requires more investigation.

I would like to emphasize though that these enhancements are not proposed by this ACP, and are entirely optional. However without this change they are essentially impossible.

Solution sketch

My suggestion is that starting immediately, we add an explicit 'static bound anywhere Any is used in the standard library. E.g. the Any trait itself immediately becomes:

pub trait Any: 'static {
    fn type_id(&self) -> TypeId
    where Self: 'static;
}

and for example downcast_ref explicitly adds a 'static bound to both dyn Any and T: Any:

impl dyn Any + 'static {
    pub fn downcast_ref<T: Any + 'static>(&self) -> Option<&T> { ... }
}

Then, in the next edition of Rust, we remove the Any: 'static bound from the Any trait itself, and add it as a supertrait of Error (and possibly others).

If a crate is compiled with edition <= 2024, Any becomes a synonym for Any + 'static. This is needed for backwards compatibility, old code might be reliant on the fact that Any implies 'static. If a crate is compiled with edition > 2024, Any simply means Any without any lifetime bound.

Alternatives

A potential alternative to this would be to introduce a new trait which is effectively "Any without 'static". This would be backwards compatible without an edition break but would ultimately be more disruptive for the ecosystem as one might have dyn Any but an API expects dyn NoStaticAnyBikeshed. The other alternative would be to do nothing, and re-implement effectively all of Any onto Error as well as any other traits meant to support both lifetimes and dyn downcasting.

Links and related work

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 Any and Error trait documentation and the linked rust-lang/rust issue 60784, then trace the standard-library uses of Any named in the proposal. Done would require a settled design for the edition transition and corresponding standard-library changes, with compatibility and downcasting behavior verified.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.