rust-lang / rust-lang/libs-team
ACP: Remove 'static from Any itself, moving the bound to the operations themselves
Nobody has claimed this yet.
- 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:
-
If we remove the
'staticbound, everything and anything implementsAny. This means it might be possible in the future to makeAnyan 'auto-supertrait', always implied by anydyn Trait. This means you can always downcast fromdyn Trait, even if no one bothered to specifyAnyexplicitly. The cost of this is slightly enlarging every singledynvtable with the information needed to downcast. C++ makes this choice, for example. -
We can possibly relax the
'staticbound away fromTypeId::ofentirely in the future. If so then you can request the type id of anydyn TraitwhereTrait : 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. -
Safe downcasting from
dyn Any + 'a, not justdyn Any + 'static. For some types you can soundly downcast to it fromdyn Any + 'afor any'a, as theTypeIdmatching lets us conclude there is no lifetime issue. This includes types such asu32andstruct Foo { name: &'static str }but not&'static stritself (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
-
https://users.rust-lang.org/t/about-the-soundess-of-a-non-static-any-trait/65830
I don't think this is too relevant as I'm not proposing relaxing
T: 'staticondowncast, which is what this focuses on. -
https://users.rust-lang.org/t/borrowing-as-any-non-static/131565
This mentions
Context::extas something that would also benefit from non-'staticAny, although I'm not too familiar with it.
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 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