Skip to content

Commit b2230e8

Browse files
authored
Merge pull request #2027 from withoutboats/object-safe-for-dispatch
Tweak object safety rules to allow static dispatch
2 parents 135c83c + 15ef4f7 commit b2230e8

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

text/2027-object_safe_for_dispatch.md

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
- Feature Name: object_safe_for_dispatch
2+
- Start Date: 2017-06-10
3+
- RFC PR: [rust-lang/rfcs#2027](https://github.com/rust-lang/rfcs/pull/2027)
4+
- Rust Issue: [rust-lang/rust#43561](https://github.com/rust-lang/rust/issues/43561)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Tweak the object safety rules to allow using trait object types for static
10+
dispatch, even when the trait would not be safe to instantiate as an object.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
Because Rust features a very expressive type system, users often use the type
16+
system to express high level constraints which can be resolved at compile time,
17+
even when the types involved are never actually instantiated with values.
18+
19+
One common example of this is the use of "zero-sized types," or types which
20+
contain no data. By statically dispatching over zero sized types, different
21+
kinds of conditional or polymorphic behavior can be implemented purely at
22+
compile time.
23+
24+
Another interesting case is the use of implementations on the dynamically
25+
dispatched trait object types. Sometimes, it can be sensible to statically
26+
dispatch different behaviors based on the name of a trait; this can be done
27+
today by implementing traits (with only static methods) on the trait object
28+
type:
29+
30+
```rust
31+
trait Foo {
32+
fn foo() { }
33+
}
34+
35+
trait Bar { }
36+
37+
// Implemented for the trait object type
38+
impl Foo for Bar { }
39+
40+
fn main() {
41+
// Never actually instantiate a trait object:
42+
Bar::foo()
43+
}
44+
```
45+
46+
However, this can only be implemented if the trait being used as the receiver
47+
is object safe. Because this behavior is entirely dispatched statically, and a
48+
trait object is never instantiated, this restriction is not necessary. Object
49+
safety only matters when you actually create a dynamically dispatched trait
50+
object at runtime.
51+
52+
This RFC proposes to lift that restriction, allowing trait object types to be
53+
used for static dispatch even when the trait is not object safe.
54+
55+
# Detailed design
56+
[design]: #detailed-design
57+
58+
Today, the rules for object safey work like this:
59+
60+
* If the trait (e.g. `Foo`) **is** object safe:
61+
- The object type for the trait is a valid type.
62+
- The object type for the trait implements the trait; `Foo: Foo` holds.
63+
- Implementations of the trait can be cast to the object type; `T as Foo`
64+
is valid.
65+
* If the trait (e.g. `Foo`) **is not** object safe:
66+
- Any attempt to use the object type for the trait is considered invalid
67+
68+
After this RFC, we will change the non-object-safe case to directly mirror the
69+
object-safe case. The new rules will be:
70+
71+
* If the trait (e.g. `Foo`) **is not** object safe:
72+
- The object type for the trait **does not** implement the trait;
73+
`Foo: Foo` does not hold.
74+
- Implementations of the trait **cannot** be cast to the object type,
75+
`T as Foo` is not valid
76+
- **However**, the object type is still a valid type. It just does not meet
77+
the self-trait bound, and it cannot be instantiated in safe Rust.
78+
79+
This change to the rules will allow trait object types to be used for static
80+
dispatch.
81+
82+
# How We Teach This
83+
[how-we-teach-this]: #how-we-teach-this
84+
85+
This is just a slight tweak to how object safety is implemented. We will need
86+
to make sure the the official documentation is accurate to the rules,
87+
especially the reference.
88+
89+
However, this does not need to be **highlighted** to users per se in the
90+
explanation of object safety. This tweak will only impact advanced uses of the
91+
trait system.
92+
93+
# Drawbacks
94+
[drawbacks]: #drawbacks
95+
96+
This is a change to an existing system, its always possible it could cause
97+
regressions, though the RFC authors are unaware of any.
98+
99+
Arguably, the rules become more nuanced (though they also become a more direct
100+
mirror).
101+
102+
This would allow instantiating object types for non-object safe traits in
103+
unsafe code, by transmuting from `std::raw::TraitObject`. This would be
104+
extremely unsafe and users almost certainly should not do this. In the status
105+
quo, they just can't.
106+
107+
# Alternatives
108+
[alternatives]: #alternatives
109+
110+
We could instead make it possible for every trait to be object safe, by
111+
allowing `where Self: Sized` bounds on every single item. For example:
112+
113+
```rust
114+
// Object safe because all of these non-object safe items are constrained
115+
// `Self: Sized.`
116+
trait Foo {
117+
const BAR: usize where Self: Sized;
118+
type Baz where Self: Sized;
119+
fn quux() where Self: Sized;
120+
fn spam<T: Eggs>(&self) where Self: Sized;
121+
}
122+
```
123+
124+
However, this puts the burden on users to add all of these additional bounds.
125+
126+
Possibly we should add bounds like this in addition to this RFC, since they
127+
are already valid on functions, just not types and consts.
128+
129+
# Unresolved questions
130+
[unresolved]: #unresolved-questions
131+
132+
How does this impact the implementation in rustc?

0 commit comments

Comments
 (0)