lang (traits | typesystem | trait-object)
Tweak the object safety rules to allow using trait object types for static dispatch, even when the trait would not be safe to instantiate as an object.
Because Rust features a very expressive type system, users often use the type system to express high level constraints which can be resolved at compile time, even when the types involved are never actually instantiated with values.
One common example of this is the use of "zero-sized types," or types which contain no data. By statically dispatching over zero sized types, different kinds of conditional or polymorphic behavior can be implemented purely at compile time.
Another interesting case is the use of implementations on the dynamically dispatched trait object types. Sometimes, it can be sensible to statically dispatch different behaviors based on the name of a trait; this can be done today by implementing traits (with only static methods) on the trait object type:
trait Foo {
fn foo() { }
}
trait Bar { }
// Implemented for the trait object type
impl Foo for Bar { }
fn main() {
// Never actually instantiate a trait object:
Bar::foo()
}
However, this can only be implemented if the trait being used as the receiver is object safe. Because this behavior is entirely dispatched statically, and a trait object is never instantiated, this restriction is not necessary. Object safety only matters when you actually create a dynamically dispatched trait object at runtime.
This RFC proposes to lift that restriction, allowing trait object types to be used for static dispatch even when the trait is not object safe.
Today, the rules for object safey work like this:
Foo
) is object safe:
Foo: Foo
holds.T as Foo
is valid.Foo
) is not object safe:
After this RFC, we will change the non-object-safe case to directly mirror the object-safe case. The new rules will be:
Foo
) is not object safe:
Foo: Foo
does not hold.T as Foo
is not validThis change to the rules will allow trait object types to be used for static dispatch.
This is just a slight tweak to how object safety is implemented. We will need to make sure the the official documentation is accurate to the rules, especially the reference.
However, this does not need to be highlighted to users per se in the explanation of object safety. This tweak will only impact advanced uses of the trait system.
This is a change to an existing system, its always possible it could cause regressions, though the RFC authors are unaware of any.
Arguably, the rules become more nuanced (though they also become a more direct mirror).
This would allow instantiating object types for non-object safe traits in
unsafe code, by transmuting from std::raw::TraitObject
. This would be
extremely unsafe and users almost certainly should not do this. In the status
quo, they just can't.
We could instead make it possible for every trait to be object safe, by
allowing where Self: Sized
bounds on every single item. For example:
// Object safe because all of these non-object safe items are constrained
// `Self: Sized.`
trait Foo {
const BAR: usize where Self: Sized;
type Baz where Self: Sized;
fn quux() where Self: Sized;
fn spam<T: Eggs>(&self) where Self: Sized;
}
However, this puts the burden on users to add all of these additional bounds.
Possibly we should add bounds like this in addition to this RFC, since they are already valid on functions, just not types and consts.
How does this impact the implementation in rustc?