Skip to content
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

Trait method impl restrictions #3678

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1a7ba23
Trait method impl restrictions
joshtriplett Jul 20, 2024
0c62bd8
RFC 3678
joshtriplett Aug 13, 2024
bbb6eaf
Future possibilities: `impl(unsafe)`
joshtriplett Aug 15, 2024
1e77b31
Add example of `Error::type_id` to motivation
joshtriplett Aug 20, 2024
25a53f9
Possible future work: integrate with stability markers
joshtriplett Aug 20, 2024
035b9f3
Move never-override syntax from future work to the body of the RFC
joshtriplett Aug 20, 2024
d6df9a3
Reword note about not placing in vtable
joshtriplett Sep 7, 2024
6bc226e
Switch to the `final` keyword
joshtriplett Sep 7, 2024
f3efb27
Rename the RFC
joshtriplett Sep 7, 2024
886a667
Future work: associated consts
joshtriplett Sep 10, 2024
60079a0
Add some further details
joshtriplett Sep 22, 2024
df3fc38
Avoid exhaustively enumerating qualifiers
joshtriplett Sep 22, 2024
79793f1
Word-wrap
joshtriplett Sep 22, 2024
13130e5
Reword descriptions of compatibility
joshtriplett Sep 22, 2024
476f4de
Clarifications about non-overridability of `final`
joshtriplett Sep 22, 2024
47eae02
Minor rewording to avoid being method-specific
joshtriplett Sep 22, 2024
931bd57
`final` has no impact on the `dyn`-compatibility of a trait
joshtriplett Sep 22, 2024
e91589f
Rephrase syntax explanation
joshtriplett Sep 22, 2024
a263847
Word-wrap and remove an extraneous detail
joshtriplett Sep 22, 2024
0c80693
Switch from `final` to `#[inherent]`
joshtriplett Oct 23, 2024
8964678
Add alternative/future possibility of inherent `impl Trait { ... }` b…
joshtriplett Oct 23, 2024
09fe432
For the alternative/future syntax, mention potential confusion with R…
joshtriplett Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions text/3678-final.md
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:

```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.

joshtriplett marked this conversation as resolved.
Show resolved Hide 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

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*.