-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Trait method impl restrictions #3678
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
Open
joshtriplett
wants to merge
22
commits into
rust-lang:master
Choose a base branch
from
joshtriplett:final
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
1a7ba23
Trait method impl restrictions
joshtriplett 0c62bd8
RFC 3678
joshtriplett bbb6eaf
Future possibilities: `impl(unsafe)`
joshtriplett 1e77b31
Add example of `Error::type_id` to motivation
joshtriplett 25a53f9
Possible future work: integrate with stability markers
joshtriplett 035b9f3
Move never-override syntax from future work to the body of the RFC
joshtriplett d6df9a3
Reword note about not placing in vtable
joshtriplett 6bc226e
Switch to the `final` keyword
joshtriplett f3efb27
Rename the RFC
joshtriplett 886a667
Future work: associated consts
joshtriplett 60079a0
Add some further details
joshtriplett df3fc38
Avoid exhaustively enumerating qualifiers
joshtriplett 79793f1
Word-wrap
joshtriplett 13130e5
Reword descriptions of compatibility
joshtriplett 476f4de
Clarifications about non-overridability of `final`
joshtriplett 47eae02
Minor rewording to avoid being method-specific
joshtriplett 931bd57
`final` has no impact on the `dyn`-compatibility of a trait
joshtriplett e91589f
Rephrase syntax explanation
joshtriplett a263847
Word-wrap and remove an extraneous detail
joshtriplett 0c80693
Switch from `final` to `#[inherent]`
joshtriplett 8964678
Add alternative/future possibility of inherent `impl Trait { ... }` b…
joshtriplett 09fe432
For the alternative/future syntax, mention potential confusion with R…
joshtriplett 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,140 @@ | ||
- Feature Name: `final` | ||
- Start Date: 2024-07-20 | ||
- RFC PR: [rust-lang/rfcs#3678](https://github.com/rust-lang/rfcs/pull/3678) | ||
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Support restricting implementation of individual methods within traits, using | ||
the already reserved `final` keyword. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
When defining a trait, the trait can provide optional methods with default | ||
implementations, which become available on every implementation of the trait. | ||
However, the implementer of the trait can still provide their own | ||
implementation of such a method. In some cases, the trait does not want to | ||
allow implementations to vary, and instead wants to guarantee that all | ||
implementations of the trait use an identical method implementation. For | ||
instance, this may be an assumption required for correctness. | ||
|
||
This RFC allows restricting the implementation of trait methods. | ||
|
||
This mechanism also faciliates marker-like traits providing no implementable | ||
methods, such that implementers only choose whether to provide the trait and | ||
never how to implement it; the trait then provides all the method | ||
implementations. | ||
|
||
One example of a trait in the standard library benefiting from this: | ||
`Error::type_id`, which has thus far remained unstable because it's unsafe to | ||
override. This RFC would allow stabilizing that method so users can call it, | ||
without permitting reimplementation of it. | ||
|
||
# Explanation | ||
[explanation]: #explanation | ||
|
||
When defining a trait, the definition can annotate methods or associated | ||
functions to restrict whether implementations of the trait can define them. For | ||
instance: | ||
|
||
```rust | ||
trait MyTrait: Display { | ||
final fn method(&self) { | ||
println!("MyTrait::method: {self}"); | ||
} | ||
} | ||
``` | ||
|
||
A method or associated function marked as `final` must have a default body. | ||
|
||
When implementing a trait, the compiler will emit an error if the | ||
implementation attempts to define any method or associated function marked as | ||
`final`, and will emit a suggestion to delete the implementation. | ||
|
||
In every other way, a `final` method or associated function acts identically to | ||
any other method or associated function, and can be invoked accordingly: | ||
|
||
```rust | ||
fn takes_mytrait(m: &impl MyTrait) { | ||
m.method(); | ||
} | ||
``` | ||
|
||
Note that in some cases, the compiler might choose to avoid placing a `final` | ||
method in the trait's vtable, if the one-and-only implementation does not | ||
benefit from monomorphization. | ||
|
||
Note that removing a `final` restriction is always forwards-compatible. | ||
|
||
The keyword `final` has been reserved since Rust 1.0, so this feature can ship | ||
identically in all editions. | ||
|
||
# Reference-level explanation | ||
[reference-level-explanation]: #reference-level-explanation | ||
|
||
At runtime, a `final fn` behaves exactly the same as a `fn`. | ||
|
||
It's a non-breaking change at the language level to turn a `final fn` into a `fn`, and thus may be done in a minor version, however care must be taken to ensure that doing so doesn't introduce soundness issues. | ||
|
||
It's a breaking change to turn `fn` into `final fn` unless the trait was already `impl(crate)` or narrower. | ||
|
||
At compile-time, a method declared as `final fn` in a trait must have a provided body and cannot be overridden in any `impl`. | ||
|
||
`final fn` cannot be combined with `default fn`. | ||
|
||
`final fn` is allowed only on methods in trait definitions, not on inherent impls nor non-trait functions nor in `extern` blocks. | ||
|
||
`final` comes after visibility but before any qualifiers such as `async`. | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
As with any language feature, this adds more surface area to the language. | ||
|
||
# Rationale and alternatives | ||
[rationale-and-alternatives]: #rationale-and-alternatives | ||
|
||
Rather than using `final`, we could use the `impl(visibility)` syntax from [RFC | ||
3323](https://rust-lang.github.io/rfcs/3323-restrictions.html). This would | ||
allow more flexibility (such as overriding a method within the crate but not | ||
outside the crate), and would be consistent with other uses of RFC 3323. On the | ||
other hand, such flexibility would come at the cost of additional complexity, | ||
and would be less familiar to people who have seen `final` in other languages. | ||
We can always add such syntax for the more general cases in the future if | ||
needed; see the future possibilities section. | ||
|
||
We could use `#[final]` rather than `final`. However, since we already have the | ||
`final` keyword reserved, using that keyword seems syntactically simpler than | ||
an attribute. | ||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
It's possible to work around the lack of this functionality by placing the | ||
additional methods in an extension trait with a blanket implementation. | ||
However, this is a user-visible API difference: the user must import the | ||
extension trait, and use methods from the extension trait rather than from the | ||
base trait. | ||
|
||
# Prior art | ||
[prior-art]: #prior-art | ||
|
||
This feature is similar to `final` methods in Java or C++. | ||
|
||
# Future possibilities | ||
[future-possibilities]: #future-possibilities | ||
|
||
We could add additional flexibility using the restriction mechanism defined in | ||
[RFC 3323](https://rust-lang.github.io/rfcs/3323-restrictions.html), using | ||
syntax like `impl(crate)` to restrict implementation of a method or associated | ||
function outside a crate while allowing implementations within the crate. | ||
(Likewise with `impl(self)` or any other visibility.) | ||
|
||
We could theoretically allow `final` restrictions on associated consts and types, as well. | ||
This seems less useful, but if it's trivial to implement we might want to | ||
support it. | ||
|
||
We could support `impl(unsafe)`, to make a trait safe to implement if *not* | ||
overriding the method, and only unsafe to implement if overriding the method. | ||
|
||
We could integrate this with stability markers, to stabilize calling a method | ||
but keep it unstable to *implement*. |
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.
Uh oh!
There was an error while loading. Please reload this page.