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 (rust-lang/rust#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 '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.
-
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.
-
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
Proposal
Problem statement
The
Anytrait has a'staticbound. This is unnecessarily restrictive. It means you can't addAnyas a supertrait to your own traits if you wish to support downcasting, unless you already have a'staticbound 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 addtype_idtodyn Error(rust-lang/rust#60784). If you look at that issue, and at a large amount of the implementations onError, we are essentially just re-implementingAnyonError. This is because we can't simply add anAnybound toErrorbecause of the'staticbound, as there might beErrortypes which contain lifetimes. This problem applies to any trait which is intended to be used indynwhile also being intended for things containing lifetimes.If we move the
'staticbound to the relevant methods/associated functions instead of the trait itself, we solve the above problem and can simply addAnyas a supertrait toError. ThenError + 'staticautomatically getstype_id. In general with this change one can freely addAnyas 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
'staticbound anywhereAnyis used in the standard library. E.g. theAnytrait itself immediately becomes:and for example
downcast_refexplicitly adds a'staticbound to bothdyn AnyandT: Any:Then, in the next edition of Rust, we remove the
Any: 'staticbound from theAnytrait itself, and add it as a supertrait ofError(and possibly others).If a crate is compiled with edition <= 2024,
Anybecomes a synonym forAny + 'static. This is needed for backwards compatibility, old code might be reliant on the fact thatAnyimplies'static. If a crate is compiled with edition > 2024,Anysimply meansAnywithout any lifetime bound.Alternatives
A potential alternative to this would be to introduce a new trait which is effectively "
Anywithout'static". This would be backwards compatible without an edition break but would ultimately be more disruptive for the ecosystem as one might havedyn Anybut an API expectsdyn NoStaticAnyBikeshed. The other alternative would be to do nothing, and re-implement effectively all ofAnyontoErroras well as any other traits meant to support both lifetimes anddyndowncasting.Links and related work
Tracking issue for stabilizing
Error::type_idrust#60784https://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.