-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Tweak object safety rules to allow static dispatch #2027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
withoutboats
merged 2 commits into
rust-lang:master
from
withoutboats:object-safe-for-dispatch
Jul 30, 2017
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| - Feature Name: object_safe_for_dispatch | ||
| - Start Date: 2017-06-10 | ||
| - RFC PR: (leave this empty) | ||
| - Rust Issue: (leave this empty) | ||
|
|
||
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| 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. | ||
|
|
||
| # Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| 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: | ||
|
|
||
| ```rust | ||
| 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. | ||
|
|
||
| # Detailed design | ||
| [design]: #detailed-design | ||
|
|
||
| Today, the rules for object safey work like this: | ||
|
|
||
| * If the trait (e.g. `Foo`) **is** object safe: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean "If it is safe then...", or "It is safe if ..."?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the first |
||
| - The object type for the trait is a valid type. | ||
| - The object type for the trait implements the trait; `Foo: Foo` holds. | ||
| - Implementations of the trait can be cast to the object type; `T as Foo` | ||
| is valid. | ||
| * If the trait (e.g. `Foo`) **is not** object safe: | ||
| - Any attempt to use the object type for the trait is considered invalid | ||
|
|
||
| After this RFC, we will change the non-object-safe case to directly mirror the | ||
| object-safe case. The new rules will be: | ||
|
|
||
| * If the trait (e.g. `Foo`) **is not** object safe: | ||
| - The object type for the trait **does not** implement the trait; | ||
| `Foo: Foo` does not hold. | ||
| - Implementations of the trait **cannot** be cast to the object type, | ||
| `T as Foo` is not valid | ||
| - **However**, the object type is still a valid type. It just does not meet | ||
| the self-trait bound, and it cannot be instantiated in safe Rust. | ||
|
|
||
| This change to the rules will allow trait object types to be used for static | ||
| dispatch. | ||
|
|
||
| # How We Teach This | ||
| [how-we-teach-this]: #how-we-teach-this | ||
|
|
||
| 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. | ||
|
|
||
| # Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| 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. | ||
|
|
||
| # Alternatives | ||
| [alternatives]: #alternatives | ||
|
|
||
| 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: | ||
|
|
||
| ```rust | ||
| // 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. | ||
|
|
||
| # Unresolved questions | ||
| [unresolved]: #unresolved-questions | ||
|
|
||
| How does this impact the implementation in rustc? | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/safey/safety