-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Supertrait Auto-impl #3851
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
base: master
Are you sure you want to change the base?
Supertrait Auto-impl #3851
Conversation
|
||
Impl blocks using auto implementations are simply a short-hand for multiple impl blocks with all of the consequences that implies. | ||
|
||
When a trait has an `auto impl` entry, all impl blocks for the trait that do not use `extern impl` to opt-out of the auto implementation become equivalent to two impl blocks, one for the sub-trait and one for the super-trait. They are generated according to these rules: |
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.
When a trait has an auto impl entry, all impl blocks for the trait that do not use extern impl to opt-out of the auto implementation become equivalent to two impl blocks, one for the sub-trait and one for the super-trait.
AFAICT, this rule makes it backwards-incompatible to add auto impls for existing traits with existing supertraits, correct? That is, we would not be able to add an auto impl PartialOrd { ... }
in Ord
without breaking existing users, as existing impls would have to have specified extern impl PartialOrd;
. I think it would be helpful for the RFC to step through some of the example use-cases (the ones discussed in the lang design meeting) and describe how they would look under this RFC.
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.
I have updated the document to address this.
We can explore the possibility of introducing relaxation so that for some cases the compiler can check further if auto impl
may overlap and bail out by assuming an implicit extern impl
statement.
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.
I am also adding the worked example on how the motivating examples from the lang design meeting would work under this RFC in the motivation section.
text/0000-supertrait-auto-impls.md
Outdated
# Unresolved questions | ||
[unresolved-questions]: #unresolved-questions | ||
|
||
- What parts of the design do you expect to resolve through the RFC process before this gets merged? |
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.
the idea is for you as the RFC writer to answer these questions by writing your own list of questions instead of these, if you have none then you can just write "None so far." or something.
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.
Understood. This was from the template text. It should have been dropped.
The explanation states that the traits need to have a super/sub trait relation. But some examples use traits e.g. |
Signed-off-by: Xiangfei Ding <[email protected]>
6c7841c
to
6114f50
Compare
text/0000-supertrait-auto-impls.md
Outdated
The reason for this is that given that `trait Subtrait` has already provided its implementation, an implementation of `Subtrait` must choose between the default implementation and a user-defined implementation. We prefer explicit confirmation through `extern impl` declaration from the implementor, rather than making the compiler to reason about whether `auto impl` should be backfilled for ease of language feature implementation. | ||
|
||
#### Extension: Possible relaxation through an unsafe attribute and a future-compatibility lint | ||
For important ecosystem traits like `PartialOrd` and `Ord`, this rule is still unsatisfactory due to [the potential rewrites required](#po-o) on downstream crates, even though it could be as small as an additional `extern impl PartialOrd`. As an extension, the rule could be relaxed with an unsafe attribute `#![unsafe(probe_extern_impl)]` and apply further trait selection to decide whether the default implementation given by the `auto impl` block should be used. |
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.
what operation is this performing that could produce UB therefore requiring unsafe
? if there is none, unsafe
should not be used.
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.
Okay, I will drop the unsafe
qualifier.
@dingxiangfei2009 Please don't force-push in this repo, that's heavily discouraged. From the README.md:
|
Quick question for @fmease, should I rename the RFC file from serial |
@dingxiangfei2009 the name won't get updated automatically. We've merged the 2025H2 RFC, forgot to update and had to open a separate PR to fix the name: #3860. |
text/3851-supertrait-auto-impls.md
Outdated
``` | ||
The reason for this is that given that `trait Subtrait` has already provided its implementation, an implementation of `Subtrait` must choose between the default implementation and a user-defined implementation. We prefer explicit confirmation through `extern impl` declaration from the implementor, rather than making the compiler to reason about whether `auto impl` should be backfilled for ease of language feature implementation. | ||
|
||
#### Extension: Possible relaxation through an unsafe attribute and a future-compatibility lint |
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.
you still have unsafe
here
text/3851-supertrait-auto-impls.md
Outdated
The reason for this is that given that `trait Subtrait` has already provided its implementation, an implementation of `Subtrait` must choose between the default implementation and a user-defined implementation. We prefer explicit confirmation through `extern impl` declaration from the implementor, rather than making the compiler to reason about whether `auto impl` should be backfilled for ease of language feature implementation. | ||
|
||
#### Extension: Possible relaxation through an unsafe attribute and a future-compatibility lint | ||
For important ecosystem traits like `PartialOrd` and `Ord`, this rule is still unsatisfactory due to [the potential rewrites required](#po-o) on downstream crates, even though it could be as small as an additional `extern impl PartialOrd`. As an extension, the rule could be relaxed with an unsafe attribute `#[probe_extern_impl]` and apply further trait selection to decide whether the default implementation given by the `auto impl` block should be used. |
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.
and here
text/3851-supertrait-auto-impls.md
Outdated
|
||
In this scenario, any item in an impl block of the sub-trait using the ambiguous name will always refer to the item from the sub-trait. This means that the only way to override the item from the super trait is to use `extern impl`. | ||
|
||
## Mandatory `external impl` declaration |
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.
## Mandatory `external impl` declaration | |
## Mandatory `extern impl` declaration |
It's possible to declare that an auto implementation is unsafe. | ||
```rs | ||
trait MySubTrait: MyTrait { | ||
unsafe auto impl MyTrait; |
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.
so, does this create an unsafe
requirement or discharge an existing unsafe
requirement? you seem to have it do both which is generally not how Rust uses unsafe
anymore, now that we have unsafe_op_in_unsafe_fn
enabled by default
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.
I see that this can be confusing.
- Inside a
trait Subtrait: .. { .. }
block,unsafe auto impl
creates anunsafe
requirement. - Inside a
impl Subtrait { .. }
block,unsafe auto impl
discharges the correspondingunsafe
requirement.
Should we adjust the "keyword" orders, so that the distinction can be more obvious?
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.
that seems less useful than what I would have expected needing unsafe
for -- something like:
/// safety: `returns_even` must always return an even integer
// unsafe means we create a requirement that impls must fulfill
pub unsafe Supertrait {
fn returns_even(&self) -> u8;
}
pub trait Subtrait: Supertrait {
/// safety: returns_even always returns an even integer
// unsafe here means we're fulfilling the requirement since this is an impl of Supertrait
unsafe auto impl Supertrait {
fn returns_even(&self) -> u8 {
self.get_int().wrapping_mul(2)
}
};
fn get_int(&self) -> u8;
}
impl Subtrait for u8 {
fn get_int(&self) -> u8 {
*self
}
}
pub struct Even(u8);
impl Subtrait for Even {
// no unsafe needed here because this isn't fulfilling or creating any safety requirements,
// the unsafe impl Supertrait below does that,
// all we're doing here is saying this impl doesn't also impl Supertrait
extern impl Supertrait;
fn get_int(&self) -> u8 {
self.0 / 2
}
}
// safety: returns_even always returns an even integer because Even contains an even integer
// unsafe here means we're fulfilling the requirement since this is an impl of Supertrait
unsafe impl Supertrait for Even {
fn returns_even(&self) -> u8 {
self.0
}
}
We would like to propose
auto impl
syntax for supertraits for a few language enhancement, to ultimately facilitate easier trait evolution and refactoring and easier trait authoring when trait hierarchy is in consideration.24/09/2025: revision published with typo fixes and dropping the
unsafe
qualifier forprobe_extern_impl
attribute.Past edit history
21/09/2025: revision published with the following changes. - Address [comments from @cramertj](https://github.com//pull/3851#discussion_r2301552062) by including the worked examples mentioned in the [design meeting](https://hackmd.io/@1n2nRkvSTd-QgK8cCPds1w/HkBmeCE7xx). A new proposal is included so that an unsafe attribute may be used to allow compiler to probe further and decide if `auto impl` default implementation would be conflicting. This might need consultation with the types team as its effectiveness and stability depends very much on the capabilities of the trait solver. - Address [comments from @ElchananHaas](https://github.com//pull/3851#discussion_r2306004253) and [comments from @N4tus](https://github.com//pull/3851#issuecomment-3239334843) on several deficiencies in the worked examples, such as missing superbounds and complexity in the motivating examples. - Address [a comment from @programmerjake](https://github.com//pull/3851#discussion_r2302020617) by removing the template text.Rendered