-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Trait method impl restrictions (final methods)
#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
Changes from 10 commits
1a7ba23
0c62bd8
bbb6eaf
1e77b31
25a53f9
035b9f3
d6df9a3
6bc226e
f3efb27
886a667
60079a0
df3fc38
79793f1
13130e5
476f4de
47eae02
931bd57
e91589f
a263847
0c80693
8964678
09fe432
4fce01f
e06c4af
79999ca
3b2edf5
b01d3d6
3efbb56
0807f41
51a9c19
c6e9279
2bf5997
e1c2981
41457ab
cff2f39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| - 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: | ||
|
joshtriplett marked this conversation as resolved.
|
||
|
|
||
| ```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. | ||
|
joshtriplett marked this conversation as resolved.
Outdated
|
||
|
|
||
| The keyword `final` has been reserved since Rust 1.0, so this feature can ship | ||
| identically in all editions. | ||
|
|
||
|
joshtriplett marked this conversation as resolved.
|
||
| # 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 | ||
|
Comment on lines
+110
to
+111
Member
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. Rather than a "Future Possibility", I see my "Explicit API Knobs" proposal as an alternative. Admittedly I don't mind
trait Trait {
#[forbid(downstream_crates(override))]
fn method() { ... }
}
Member
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. fwiw the syntax for restrictions was/is an unresolved question. I wouldn't mind moving towards
Member
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. In that case the RFC is very much the type of uses I had in mind!
Member
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. I don't feel represented by the current text, fwiw. The current text does mention RFC 3323 but only as an alternative keyword syntax. The aspect I care about is that API control be based on reusable+composable concepts. Neither
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. This isn't explicitly relevant to this discussion, but I find it very funny that the counterproposal to syntax being added is an attribute being added, when that is exactly the opposite of where The main reason I proposed an attribute is because no one could decide how the syntax should work, and I had a simple idea for something that would be relatively memorable and figured it could be extended later, either via syntax or further refining the attribute. A lot of people mentioned how a dedicated syntax would very naturally extend to the various privacy specifiers that Rust has, which the attribute currently does not support. (It's a crate-global property.) So, I guess that my point here is to also add that as similar prior art for, ultimately, it doesn't really matter whether we have syntax or an attribute; the ecosystem can deal with either, and the solution is whatever people are more likely to support right now. After the initial implementation is out there, people will give you plenty of ideas to extend it, and having most of what you need implemented in the compiler already will make it easier to experiment with unstable extensions.
Member
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. That is quite funny x) |
||
|
|
||
| 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.
Outdated
|
||
|
|
||
| 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++. | ||
|
joshtriplett marked this conversation as resolved.
|
||
|
|
||
| # 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*. | ||
Uh oh!
There was an error while loading. Please reload this page.