From 3d23bcceaad61eb9ef90793c4db389c18b0937ae Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Tue, 26 Aug 2025 14:02:53 +0200 Subject: [PATCH 01/14] supertrait auto impl RFC v2 Signed-off-by: Xiangfei Ding --- text/0000-supertrait-auto-impls.md | 642 +++++++++++++++++++++++++++++ 1 file changed, 642 insertions(+) create mode 100644 text/0000-supertrait-auto-impls.md diff --git a/text/0000-supertrait-auto-impls.md b/text/0000-supertrait-auto-impls.md new file mode 100644 index 00000000000..ed9fc6653a0 --- /dev/null +++ b/text/0000-supertrait-auto-impls.md @@ -0,0 +1,642 @@ +- Feature Name: `supertrait_auto_impl` +- Start Date: 2025-08-26 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +We would like to allow nested trait `impl` blocks in `trait` defintion blocks and `impl Trait for` blocks, so that users can supply supertrait items in subtrait contexts. + +```rust +trait Supertrait1 { + type Type; +} +trait Supertrait2 { + type Type1; + type Type2; +} +trait Subtrait: Supertrait1 + Supertrait2 { + auto impl Supertrait1; + auto impl Supertrait2 { + type Type1 = Self; + type Type2 = (); + } +} +impl Subtrait for MyType { + type Type = u8; // implicitly implements Supertrait1::Type + + auto impl Supertrait2; // This generates an implicit `impl Supertrait2 for MyType` as backfill +} +``` + +# Motivation +[motivation]: #motivation + +## Helpers for implementing traits + +This is not the main motivation for this RFC, but it is a secondary additional feature that this RFC enables. + +For some traits, it's difficult to implement the trait directly because the "raw" interface that the trait exposes is complex. If the trait is unsafe, this may be even worse as the end-user may not wish to write any unsafe code. This feature makes it easy to provide utilities for more easily implementing the trait. + +### Example: Serde + +The serde traits are notoriously difficult to implement directly. It's almost always done by macro. Imagine if you could write this: +```rs +struct MyStruct { + name: String, + int: i32, +} + +#[derive(Serialize)] +struct MyStructProxy<'a> { + name: &'a str, + tens: i32, + digit: i32, +} + +impl SerializeByProxy for MyStruct { + type Proxy<'a> = MyStructProxy<'a>; + fn serialize(&self) -> MyStructProxy<'_> { + Proxy { + name: &self.name, + digit: self.int % 10, + tens: self.int / 10, + } + } +} +``` +And then `MyStruct` automatically implements `Serialize` by creating a `MyStructProxy` instance and serializing the proxy. So for example `MyStruct { name: "a", int: 42 }` is serialized into json as `{"name":"a","tens":4,"digit":2}`. + +Right now, the only way to provide a helper like the one above is to either: + +* Implement a proxy that emits the `Serialize` impl block, or +* Provide helper methods and instruct the user how to manually implement `Serialize` using the helpers you provided. + + + +
+Hypothetical example: Evolving the `Deref` trait + + This section is included because whether Deref is to be merged with Receiver is up for deliberation at the moment. + +[deref-receiver]: #Deref-Receiver-evolution + +As part of the `arbitrary_self_types` feature, we need to split `Deref` into two traits. Right now, the `Deref` trait looks like this: +```rs +pub trait Deref { + type Target: ?Sized; + + fn deref(&self) -> &Self::Target; +} +``` +But we need it to look like this: +```rs +pub trait Receiver { + type Target: ?Sized; +} + +pub trait Deref: Receiver { + fn deref(&self) -> &Self::Target; +} +``` +However, making this change is difficult due to backwards compatibility. There are many crates in the ecosystem with code that looks like this: +```rs +struct MyStruct(u8); + +impl Deref for MyStruct { + type Target = u8; + fn deref(&self) -> &u8 { + &self.0 + } +} +``` +or like this: +```rs +fn assert_deref() +where + MyStruct: Deref +{} +``` +The feature in this RFC provides a mechanism for _trait evolution_ where it becomes possible to split a trait into a super-trait and sub-trait without breaking backwards compatibility in downstream crates. + +### Why not a blanket impl? + +Unfortunately, this does not work: +```rs +pub trait Receiver { + type Target: ?Sized; +} + +pub trait Deref: Receiver { + type Target: ?Sized; + fn deref(&self) -> &Self::Target; +} + +impl Receiver for T { + type Target = ::Target; +} +``` +The problem is that crates need to be able to write this code: +```rs +struct SmartPtr(*mut T); + +impl Receiver for SmartPtr { + type Target = T; +} + +impl Deref for SmartPtr { + fn deref(&self) -> &T { + unsafe { &*self.0 } + } +} +``` +This kind of code where `SmartPtr` *sometimes* implements `Deref` but *always* implements `Receiver` is not possible with a blanket implementation. The feature proposed by this RFC makes the above possible. +
+ +## Tenets + +- Backward-compatibility + - + - We need to maintain that all the current `impl` blocks to compile, specifically the `impl Deref` litmus test mentioned in the [deref-receiver] motivating example. + - This demand also extends to other possible library traits that may see relocation of items into a future supertrait, while ensuring that the existing `impl` blocks continue to compile. +- Intuitional readability and clear syntatical signal + - + - We strive for a syntax that is intuitional and easily connected to the existing constructs. + - A successful design is one that enables a user to easily build a correct mental picture of the trait `impl`s with assistance from the syntatical features. + - By extension, supertrait `impl`s should appear clearly in connection with subtrait `impl`s. +- Flexibility + - + - We strive for providing users the means to refactor their traits without compromising the expressiveness of the trait relationships. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +This section gives an overview of the `auto impl` feature and some example use-cases. + +## Overview + +It is possible to declare that implementations of a sub-trait should automatically implement a given supertrait. +```rs +trait MyTrait { + fn my_func(&self); +} + +trait MySubTrait: MyTrait { + auto impl MyTrait; + + fn my_second_func(&self); +} +``` +Given the above traits, when you implement `MySubTrait`, you must also specify items from `MyTrait`. +```rs +impl MySubTrait for String { + fn my_func(&self) { + println!("my_func on String"); + } + + fn my_second_func(&self) { + println!("my_second_func on String"); + } +} +``` +In the above case, it is an error to not specify all items from `MyTrait`. However, it is possible to opt-out of implementing `MyTrait` in the `impl MySubTrait` block: +```rs +impl MyTrait for String { + fn my_func(&self) { + println!("my_func on String"); + } +} +impl MySubTrait for String { + extern impl MyTrait; + fn my_second_func(&self) { + println!("my_second_func on String"); + } +} +``` +The `extern impl MyTrait` declaration specifies the impl block does not automatically implement `MyTrait`, and that another impl block is used instead. + +### Example: Trait evolution + +Over time, needs have arised to establish a hierarchy of traits, so that the parts and pieces of existing "big" library traits, be it from `std` or ecosystem crates, can be extraced and pulled back into supertraits without requiring a breaking change in the downstream crates. In most cases, the assoication of methods to be "refactored" and the destination supertraits can be determined without ambiguity. This falls under a bigger theme of trait evolution, which concerns how a historically big trait can be broken down and refined into smaller traits and trait hierarchy. [RFC 1210](https://rust-lang.github.io/rfcs/1210-impl-specialization.html#the-default-keyword) provided an example of a trait evolution and how specialisation could have eased the refactoring. + +With this proposal, specialisation is not required and, instead, the pulled-back supertrait implementation applies directly within the context of the subtrait implementation. + +```rs= +// A refactored Subtrait that encodes parts of its protocol +// to be implementable on other types, +// to which Subtrait is not applicable. +trait Supertrait { + // This method is pulled out of Subtrait + fn supertrait_fn(); +} + +// Subtrait is the refinement of the Supertrait protocol +trait Subtrait: Supertrait { + // Refactored into the Supertrait + //*** fn supertrait_fn(); + fn subtrait_fn(); +} + +// The proposal called for enabling specialisation of +// the following blanket implementation as *stop-gap*. +// We propose that for trait evolution, we do not need to rely on specialisation... +/** +impl Supertrait for T { + default fn supertrait_fn() { + ::supertrait_fn(); + } +} +**/ + +struct Middeware(T); + +// ... but rather keep the current and future `impl` block +// the same, +impl Subtrait for Middleware { + // because the trait has been well designed so that + // it allows seamless migration. + fn supertrait_fn() { .. } + fn subtrait_fn() { .. } +} +``` + + + +## Default auto implementations + +It is possible to provide default implementations of functions from the super trait. +```rs +trait MyTrait { + fn my_func(&self); +} + +trait MySubTrait: MyTrait { + auto impl MyTrait { + fn my_func(&self) { + self.my_second_func(); + self.my_second_func(); + } + } + + fn my_second_func(&self); +} +``` +In this case, an impl block for `MySubTrait` will still automatically implement `MyTrait`, but you are not required to provide an implementation of `my_func` since a default implementation exists. + +### Example: Helpers for implementing a trait + +With default auto implementations, it becomes possible to provide a sub-trait whose purpose is to help you implement the super trait in a specific way. + +For example, given this super trait: +```rs +trait EventHandler { + type Event; + fn handle_event(&mut self, event: Self::Event); +} +``` +Then you might have a helper for implementing `EventHandler` for a specific event type: +```rs +enum MouseEvent { + ClickEvent(ClickEvent), + MoveEvent(MoveEvent), +} + +trait MouseEventHandler { + auto impl EventHandler { + type Event = MouseEvent; + fn handle_event(&mut self, event: MouseEvent) { + use MouseEvent::*; + match event { + ClickEvent(evt) => self.click_event(evt), + MoveEvent(evt) => self.move_event(evt), + } + } + } + + fn click_event(&mut self, event: ClickEvent); + fn move_event(&mut self, event: MoveEvent); +} +``` +This allows crates to implement `EventHandler` by writing this code: +```rs +struct PrintHandler; + +impl MouseEventHandler for PrintHandler { + fn click_event(&mut self, event: ClickEvent) { + println!("Click: {:?}", event); + } + fn move_event(&mut self, event: MoveEvent) { + println!("Move: {:?}", event); + } +} +``` +The `MouseEventHandler` trait could even come from a different crate than `EventHandler`. + +## Unsafe auto impl + +It's possible to declare that an auto implementation is unsafe. +```rs +trait MySubTrait: MyTrait { + unsafe auto impl MyTrait; + + fn my_second_func(&self); +} +``` +This means that it is unsafe to override the auto implementation. +```rs +impl MySubTrait for String { + // unsafe is required here because the `auto impl` + // is marked unsafe. + unsafe extern impl MyTrait; + + fn my_second_func(&self) { + println!("my_second_func on String"); + } +} +``` +If the super trait is unsafe, then the `auto impl` must also be unsafe. + +### Example: Safely implement unsafe trait + +This can be used to provide a safe way to implement an unsafe trait. +```rs +/// Implementers must ensure that even() returns an +/// even number. +unsafe trait Even { + /// Guaranteed to return an even number. + fn even(&self) -> usize; +} + +trait Double { + // SAFETY: 2*x is always even + unsafe auto impl Even { + fn even(&self) -> usize { + 2*self.value_to_double() + } + } + fn value_to_double(&self) -> usize; +} + +// provides an impl for Even safely +impl Double for String { + fn value_to_double(&self) -> usize { + self.len() + } +} +``` + +--- + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +In a trait declaration, you may declare one or more `auto impl` items with a block that provides implementations for one or more items from the super trait. +```rs +trait SubTrait: SuperTrait { + auto impl SuperTrait { + const MY_CONST: u32 = 10; + type MyType = String; + fn my_func(&self) -> u32 { + 42 + } + } +} +``` +All items inside the `auto impl` block must match an item from the super trait of the same name and signature. When the block is empty, it is legal to use a semicolon instead. That is, these are equivalent: + +* `auto impl SuperTrait {}` +* `auto impl SuperTrait;` + +The type of the super trait can be anything that matches the [TypePath](https://doc.rust-lang.org/reference/paths.html#grammar-TypePath) grammar and evaluates to a trait. This means that, for example, this is legal: +```rs +trait SubTrait { + auto impl MyGenericTrait; +} +``` +The trait must be a super-trait. That is, the trait solver must be able to prove that `where T: SubTrait` implies `where T: SuperTrait`. + +## Impl blocks + +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: + +* Both impl blocks have the exact same set of generic items and where clauses, except that in the super trait any generic parameters that are unused by the super trait's `auto impl` are omitted. +* The equivalent impl block for the super trait contains the items in the `impl` block that come from the super trait, plus any items specified in the block of the `auto impl` (if any). In case of duplicates, the item from the `impl` block is preferred. +* The equivalent impl block for the sub-trait contains any remaining items in the original `impl` block, plus an `extern impl` declaration. + +So for example, given these traits: + +```rs +trait SuperTrait { + fn my_first_item(&self, arg: U); + fn my_default_item(&self, arg: T); +} + +trait SubTrait: SuperTrait { + auto impl SuperTrait { + fn my_default_item(&self, arg: T) {} + } + fn my_second_item(&self, arg: T); +} +``` +then this impl block: +```rs +impl SubTrait for MyStruct +where + T: MyTraitBound, +{ + fn my_first_item(&self, arg: u32) {} + fn my_second_item(&self, arg: T) {} +} +``` +is short-hand for this: +```rs +// The original impl block MINUS the super-trait's methods +// PLUS an extern impl statement: +impl SubTrait for MyStruct +where + T: MyTraitBound, +{ + extern impl SuperTrait; + + fn my_second_item(&self, arg: T) {} +} + +// PLUS an additional impl block for the super trait, +// constructed in the following manner: +impl SuperTrait for MyStruct +// ^^^^^^^^^^^^^^^^^^ taken verbatim from `auto impl` statement. +where + T: MyTraitBound, +{ + // With SuperTrait's methods from the impl block + fn my_first_item(&self, arg: u32) {} + + // AND SuperTrait's methods from the auto impl block + fn my_default_item(&self, arg: T) {} +} +``` +Note that this implies that you *must* use `extern impl` to provide your own implementation of the super trait. If you don't, then by the above rule, the generated impl for the super trait would overlap with your custom implementation, which is illegal by the standard trait rules. + +## Unsafe auto implementations + +The `auto impl` items can be marked `unsafe`, which declares that implementing the sub-trait without using the auto implementation is unsafe. + +When an `auto impl` is declared unsafe, then: + +* To opt-out, you must write `unsafe extern impl`. +* If any methods from the `unsafe auto impl` block are overridden, then the `impl` block must be `unsafe`. + +If the super trait is `unsafe`, then the `auto impl` declaration must also be `unsafe`. + +## Naming ambiguity + +If the sub-trait defines an item of the same name as an item in the super-trait, then the `auto impl` block must provide a default implementation of the item from the super trait. + +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`. + +--- + +# Drawbacks +[drawbacks]: #drawbacks + +- This is yet another new language syntax to teach. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## A `auto impl` in a `trait` definition block should not be interpreted as a blanket `impl` + +Our rationale is that this blanket `impl` would unnecessarily reject genuine user-`impl`s of supertraits where a relaxed `where` bounds are desirable through overlapping `impl`s. In our opinion, a supertrait `impl` on a type that has more relaxed bounds than that on a subtrait `impl` of the same type is perfectly valid. + +As illustration, the following is an example + +```rust= +trait BaseFunction { + fn base_capability(&self); +} +trait ManagementExtension: BaseFunction { + auto impl BaseFunction; + fn management_interface(&self); +} + +struct ManagementInterface { .. } + +impl BaseFunction for ManagementInterface { + // NOTE: we do not need `T: ManagementHandle` + // for ManagementInterface to provide + // `base_capability` + fn base_capability(&self) { .. } +} + +// Suppose that `auto impl BaseFunction` is "lowered" into a blanket `impl`, +// then it is impossible for the `impl BaseFunction` to compile. + +impl ManagementExtension for MangementInterface +where T: ManagementHandle +{ + extern impl BaseFunction; + fn management_interface(&self) { .. } +} +``` + +## Why explicit opt-in/out for marker traits and traits with only default items? + +Marker supertraits and supertraits with `default` items can be easily overlooked when users write subtrait implementations. They would register too little signal for the reader to recognise the significance of traits of these kinds. For this reason, we bias towards asking users to provide clear syntatical signals through `auto impl MarkerTrait` or `extern impl MarkerTrait`, so that the automatic derivation of such traits is easily recognisable and provides obvious site for documentation in case justification is waranted. + +```rs= +// It is almost always a good idea to explain +// why a trait like below is implemented on a type. +trait MarkerTrait {} + +trait Supertrait: MarkerTrait { + auto impl MarkerTrait; +} + +impl Supertrait for MyType { + // Here it is a good place to explain + // why MyType: MarkerTrait + auto impl MarkerTrait; + // or otherwise `extern impl MarkerTrait;` is required +} +``` + +### What about checking whether another impl exists? + +We could potentially determine whether the opt-out is used based on whether an impl of the super trait exists, but we prefer not to. We have existing mechanism to determine the specialisation and implementation overlaps. Whether a supertrait `impl` overlaps or not, is not the concern of this proposal. + +If we would deduce whether an `auto impl` should be effected, there could present a hazard that silently changes the program behaviour. + +```rs +trait MyTrait { default fn .. } + +trait AutoMyTrait: MyTrait { + auto impl MyTrait; +} + +// Suppose we allow users to elide the `auto impl`/`extern impl` directive +// and we deduce based on some applicability criterion ... +impl AutoMyTrait for Foo {} // <-- generates MyTrait + +// Suppose this item is added at one point of time +impl MyTrait for T { .. } + +impl MyOtherTrait for Foo {} // <-- now it should not be generated anymore +``` + +## Why is a hard error on ambiguity acceptable to us? + +We hold the basic assumption that most associated items of a trait have sensible names. We would rather advise that one shall avoid name clashes and ambiguity through better, future-oriented trait designs. + +In any case, `auto impl Trait { .. }` blocks still remains available for cases where ambiguity is unavoidable or favorable in niche scenario. + +## Why this naming? + +It is still up for discussion. + +## Why use `unsafe` in this way + +As "default" supertrait implementation, _not in specialisation sense_, can be supplied in the subtrait definition, such implementation could be essential for the safe use of the subtrait `impl`. We should enable the subtrait authors to ensure that the safety invariants encoded in those "default" supertrait implementation are also observed, if the downstream implementor decides to supply its own supertrait `impl` instead of automatic derivation. + +# Prior art +[prior-art]: #prior-art + + +## Implementable trait-alias + +It was suggested in the [RFC 3437](https://github.com/rust-lang/rfcs/pull/3437) that trait aliases can be made to also carry associated items, which in turn can be instantiated in `impl` trait alias blocks. + +## Plain supertrait items in subtrait `impl` + +In fact, this proposal is an improved version over this scheme. Previously, to disambiguate names from different supertrait namespaces, one appends the associated item identifies with a qualification and generic arguments when necessary. However, the old proposal would apply more deduction, by the compiler, on whether supertrait `impl`s are demanded and it is a weaker response to the tenet that prefers more syntatical signals to compiler deduction. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- What parts of the design do you expect to resolve through the RFC process before this gets merged? +- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? + +# Future possibilities +[future-possibilities]: #future-possibilities + +Think about what the natural extension and evolution of your proposal would +be and how it would affect the language and project as a whole in a holistic +way. Try to use this section as a tool to more fully consider all possible +interactions with the project and language in your proposal. +Also consider how this all fits into the roadmap for the project +and of the relevant sub-team. + +This is also a good place to "dump ideas", if they are out of scope for the +RFC you are writing but otherwise related. + +If you have tried and cannot think of any future possibilities, +you may simply state that you cannot think of anything. + +Note that having something written down in the future-possibilities section +is not a reason to accept the current or a future RFC; such notes should be +in the section on motivation or rationale in this or subsequent RFCs. +The section merely provides additional information. From 6114f50f22cb934105c1dc29ec723339bb3ec30f Mon Sep 17 00:00:00 2001 From: wieDasDing <6884440+dingxiangfei2009@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:57:25 +0200 Subject: [PATCH 02/14] apply suggestions and propose extensions --- text/0000-supertrait-auto-impls.md | 376 +++++++++++++++++++++++++++-- 1 file changed, 356 insertions(+), 20 deletions(-) diff --git a/text/0000-supertrait-auto-impls.md b/text/0000-supertrait-auto-impls.md index ed9fc6653a0..0dc63955236 100644 --- a/text/0000-supertrait-auto-impls.md +++ b/text/0000-supertrait-auto-impls.md @@ -16,23 +16,189 @@ trait Supertrait2 { type Type1; type Type2; } -trait Subtrait: Supertrait1 + Supertrait2 { +trait Subtrait1: Supertrait1 { auto impl Supertrait1; +} +impl Subtrait1 for MyType { + type Type = u8; // implicitly implements `Supertrait1::Type := u8` +} + +trait Subtrait2: Supertrait2 { auto impl Supertrait2 { type Type1 = Self; type Type2 = (); } } -impl Subtrait for MyType { - type Type = u8; // implicitly implements Supertrait1::Type - - auto impl Supertrait2; // This generates an implicit `impl Supertrait2 for MyType` as backfill +impl Subtrait2 for MyType { + // An implicit `impl Supertrait2 for MyType` is generated + // with `Type1 := MyType` and `Type2 := ()` as backfill } ``` # Motivation [motivation]: #motivation +## Trait evolution + +Trait evolution is a treatment to existing trait hierarchy in a library. Difficulty has arised in the past that hoisting items from the current trait into a new supertrait, or introduction of a second trait. + +This RFC promises to improve the situation around trait evolution. It captures the common cases under this theme and aims to reduce rewrites in downstream crates, should the need to re-organise trait hierarchy arises. + +### Example: trait refinement by item hoisting into supertraits + +As library code grows, there is frequently a need to breakdown a big trait into several smaller trait. This would have been a breaking change in view of SemVer and a user code rewrite is mandatory. However, the aim of this RFC is to ease the transition of downstream trait implementors to the new trait hierarchy by reducing the rewrites. + +Suppose that we start with a big trait `Subtrait` and it becomes desirable that `candidate_for_hoisting` method is hoisted into another trait. +```rust +trait Subtrait { + fn candidate_for_hoisting(&self); + fn subtrait_method(&self); +} +// downstream crate +impl Subtrait for MyType { + fn candidate_for_hoisting(&self) { .. } + fn subtrait_method(&self) { .. } +} +fn assert(x: impl Subtrait) +where + MyType: Subtrait +{ + x.candidate_for_hoisting() +} +``` +With this RFC, it is possible for the library author to perform the following refactor. +```rust +trait Supertrait { + fn candidate_for_hoisting(&self); // <~ hoisted +} +trait Subtrait: Supertrait { + auto impl Supertrait; + fn subtrait_method(&self); +} + +// downstream crate: no rewrites are required +impl Subtrait for MyType { + fn candidate_for_hoisting(&self) { .. } + // ^ this is resolved as implementor of `Supertrait::candidate_for_hoisting` + + fn subtrait_method(&self) { .. } +} +fn assert(x: impl Subtrait) +where + MyType: Subtrait +{ + x.candidate_for_hoisting() // <~ method resolves to `Supertrait::candidate_for_hoisting` +} +``` + +### Example: relaxed bounds via new supertraits +A common use case of supertraits is weaken bounds involved in associated items. There are occassions that a weakend supertrait could be useful. Suppose that we have a factory trait in the following example. In this example, the `async fn make` factory method could be weakened so that the future returned could be used in the context where the future is not required to be of `Send`. This has been enabled through the use of [the `trait_variant` crate](https://docs.rs/trait-variant/latest/trait_variant/). + +```rust +#[trait_variant::make(IntFactory: Send)] +trait LocalIntFactory { + async fn make(&self) -> i32; + fn stream(&self) -> impl Iterator; + fn call(&self) -> u32; +} + +// `trait_variant` will generate a conceptual subtrait: + +trait IntFactory: Send { + fn make(&self) -> impl Future + Send; + fn stream(&self) -> impl Iterator + Send; + fn call(&self) -> u32; +} +``` + +This RFC enables one to construct the trait in the following fashion. +```rust +trait LocalIntFactory { + async fn make(&self) -> i32; + fn stream(&self) -> impl Iterator; + fn call(&self) -> u32; +} +trait IntFactory: Send { + auto impl LocalIntFactory { + async fn make(&self) -> i32 { + IntFactory::make(self).await + } + fn stream(&self) -> impl Iterator { + IntFactory::stream(self) + } + fn call(&self) -> u32 { + IntFactory::call(self) + } + } + fn make(&self) -> impl Future + Send; + fn stream(&self) -> impl Iterator + Send; + fn call(&self) -> u32; +} +``` + +### Example: automatic supertrait implementation + +A second prominent example is the `PartialOrd` and `Ord` traits. +```rust +trait PartialOrd { + fn partial_cmp(&self, other: &Rhs) -> Option; + // ... +} +trait Ord: PartialOrd { + fn cmp(&self, other: &Rhs) -> Ordering; + // ... +} +// This is one of the more probably implementation: +impl PartialOrd for X { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for X { + fn cmp(&self, other: &Self) -> Ordering { + // here it defines a total ordering + .. + } +} +``` + +The `PartialOrd` trait could be reworked in this proposal as follows. +```rust +trait PartialOrd { + fn partial_cmp(&self, other: &Rhs) -> Option; +} +trait Ord: PartialOrd { + fn cmp(&self, other: &Rhs) -> Ordering; + auto impl PartialOrd { + fn partial_cmp(&self, other: &Rhs) -> Option { + Some(self.cmp(other)) + } + } +} +``` +There are now two choices for type `X` in the downstream crate. +- Delete the `impl PartialOrd for X`. Without the overlapping `impl`, the `auto impl` can stand in and take effect. +```rust +// delete: impl PartialOrd for X { .. } +impl Ord for X { + fn cmp(&self, other: &Rhs) -> Ordering { + // here it defines the same total ordering + } +} +``` +- Declare use of the existing applicable `impl PartialOrd for X`. +```rust +impl PartialOrd for X { + // this is the same implementation +} +impl Ord for X { + extern impl PartialOrd; + fn cmp(&self, other: &Rhs) -> Ordering { + // here it defines the same total ordering + } +} +``` + ## Helpers for implementing traits This is not the main motivation for this RFC, but it is a secondary additional feature that this RFC enables. @@ -55,8 +221,27 @@ struct MyStructProxy<'a> { digit: i32, } +trait SerializeByProxy: Serialize { + // See https://docs.rs/serde/latest/serde/trait.Serialize.html + auto impl Serialize { + fn serialize(&self, serializer: S) -> Result { + // First construct the proxy + let proxy = SerializeByProxy::serialize(self); + // Then delegate the serialization to the proxy + proxy.serialize(serializer) + } + } + + type Proxy<'a>: Serialize + where Self: 'a; + + fn serialize(&self) -> Proxy<'_>; +} + impl SerializeByProxy for MyStruct { - type Proxy<'a> = MyStructProxy<'a>; + type Proxy<'a> = MyStructProxy<'a> + where Self: 'a; + fn serialize(&self) -> MyStructProxy<'_> { Proxy { name: &self.name, @@ -64,6 +249,8 @@ impl SerializeByProxy for MyStruct { tens: self.int / 10, } } + // Now `MyStruct` is also automatically `Serialize` + // via a proxy. } ``` And then `MyStruct` automatically implements `Serialize` by creating a `MyStructProxy` instance and serializing the proxy. So for example `MyStruct { name: "a", int: 42 }` is serialized into json as `{"name":"a","tens":4,"digit":2}`. @@ -118,7 +305,36 @@ where MyStruct: Deref {} ``` -The feature in this RFC provides a mechanism for _trait evolution_ where it becomes possible to split a trait into a super-trait and sub-trait without breaking backwards compatibility in downstream crates. +The feature in this RFC provides a mechanism for _trait evolution_ of `Deref` and `Receiver` where it becomes possible to split a trait into a super-trait and sub-trait without breaking backwards compatibility in downstream crates. + +```rust +pub trait Receiver { + type Target: ?Sized; +} + +pub trait Deref: Receiver { + auto impl Receiver; + + fn deref(&self) -> &Self::Target; +} + +// Note that the `impl Deref` block is still compiled and completely equivalent +// to the definition prior to applying this RFC. +impl Deref for MyStruct { + type Target = u8; + fn deref(&self) -> &u8 { + &self.0 + } +} + +fn assert_deref() +where + MyStruct: Deref +{ + // The `Deref` predicate resolves the name `Target` to + // `Receiver::Target` under this RFC +} +``` ### Why not a blanket impl? @@ -151,7 +367,7 @@ impl Deref for SmartPtr { } } ``` -This kind of code where `SmartPtr` *sometimes* implements `Deref` but *always* implements `Receiver` is not possible with a blanket implementation. The feature proposed by this RFC makes the above possible. +This kind of code where `SmartPtr` *sometimes* implements `Deref` but *always* implements `Receiver` is not possible with a blanket implementation. The feature proposed by this RFC would make the above construction a possibility. ## Tenets @@ -222,7 +438,7 @@ Over time, needs have arised to establish a hierarchy of traits, so that the par With this proposal, specialisation is not required and, instead, the pulled-back supertrait implementation applies directly within the context of the subtrait implementation. -```rs= +```rust // A refactored Subtrait that encodes parts of its protocol // to be implementable on other types, // to which Subtrait is not applicable. @@ -302,7 +518,7 @@ enum MouseEvent { MoveEvent(MoveEvent), } -trait MouseEventHandler { +trait MouseEventHandler: EventHandler { auto impl EventHandler { type Event = MouseEvent; fn handle_event(&mut self, event: MouseEvent) { @@ -368,11 +584,11 @@ unsafe trait Even { fn even(&self) -> usize; } -trait Double { +trait Double: Even { // SAFETY: 2*x is always even unsafe auto impl Even { fn even(&self) -> usize { - 2*self.value_to_double() + 2 * self.value_to_double() } } fn value_to_double(&self) -> usize; @@ -481,6 +697,26 @@ where ``` Note that this implies that you *must* use `extern impl` to provide your own implementation of the super trait. If you don't, then by the above rule, the generated impl for the super trait would overlap with your custom implementation, which is illegal by the standard trait rules. +### Extension: item aliasing in `auto impl` +To reduce possible verbosity, we can propose a future extension to `auto impl` default implementation block, in case supertrait items can be implemented as an alias to subtrait items. + +When a subtrait associated method has the same signature as a supertrait associated method in terms of generics, has a set of `where` bounds that satisfies the supertrait item `where` bounds and compatible function signature, the `auto impl` default implementation can be simplified into a assignment statement, instead of a complete function body with a delegation call. + +```rust +trait Supertrait { + fn method(&self) -> impl Trait1; +} + +trait Subtrait: Supertrait { + auto impl Supertrait { + fn method(&self) -> impl Trait1 = ::method; + } + + fn method(&self) -> impl Trait1 + Send + where Self: 'static + Send; +} +``` + ## Unsafe auto implementations The `auto impl` items can be marked `unsafe`, which declares that implementing the sub-trait without using the auto implementation is unsafe. @@ -498,6 +734,100 @@ If the sub-trait defines an item of the same name as an item in the super-trait, 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 + +For the following definition, a **non-marker** trait is a trait with an item, which can be `default` or not. A marker trait has zero associated items. + +| Supertrait kind | `auto impl` block in sub-`trait` block | `auto impl` statement in sub-`trait` block | +|-|-|-| +| non-marker | Mandatory[a](#e-i-a) | Optional[b](#e-i-b) | +| marker Mandatory[c](#e-i-c) | + +### Case a: `auto impl` block of a non-marker supertrait in sub-`trait` block +For illustration, here is an example. +```rust +trait Supertrait { + type Item; +} +trait Subtrait: Supertrait { + auto impl Supertrait { + type Item = u32; + } +} + +impl Supertrait for MyStruct { + type Item = u8; +} +impl Subtrait for MyStruct { + // Without the following ... + extern impl Supertrait; + // ... the code will be rejected for overlapping `impl Supertrait`s +} +``` +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. + +```rust +trait Ord: PartialOrd { + #[unsafe(probe_extern_impl)] + auto impl PartialOrd { + // ... + } +} + +// The following code in the ecosystem will continue to compile. +impl PartialOrd for MyType { .. } +impl Ord for MyType { + // Given the current facts about `MyType`, + // the compiler can deduce that `MyType: PartialOrd` is satisfiable, + // so the `auto impl PartialOrd` is not used +} +``` + +However, this practice will not be encouraged eventually under provision of this RFC. For this reason, we also propose a future-compatibility lint, which will be escalated on a future Edition boundary to denial. The lint shall highlight the existing `auto impl` block in the subtrait definition and suggest an explicit `extern impl` statement in the subtrait implementation. + +### Case b: `auto impl` block of a non-marker supertrait in sub-`trait` statement +For illustration, here is an example. +```rust +trait Supertrait { + type Item; +} +trait Subtrait: Supertrait { + auto impl Supertrait; +} + +impl Supertrait for MyStruct { + type Item = u8; +} +impl Subtrait for MyStruct { + // The following `extern impl` is optional + extern impl Supertrait; +} +``` +The reason for this is that `trait Subtrait` has not already provided its implementation, an implementation of `Subtrait` must supply an implementation of `Supertrait`, which could have existed before introducing the `auto impl Supertrait`. + +If `auto impl` statement is declared on a non-marker supertrait without a default implementation, the `extern impl` is optional so that we do not penalise the existing trait implementors. + +### Case c: `auto impl` block of a marker supertrait +For illustration, here is an example. +```rust +trait Supertrait {} +trait Subtrait: Supertrait { + auto impl Supertrait; +} + +impl Subtrait for MyStruct { + // The implementor must choose between + // - `auto impl Super` and + // - `extern impl Supertrait`. + // Without either, it would be rejected with unsatisfied super-bound + extern impl Supertrait; +} +``` +The reason for this is that `Supertrait` as a marker trait has no associated items. As we could not decide if the `Supertrait` would be implemented within the bounds attached to the `impl Subtrait` block, due to lack of syntatical signals, it is better to require explicit confirmation from the implementor on the condition of the marker trait `Supertrait` when this marker is applicable to `MyStruct`. + --- # Drawbacks @@ -514,7 +844,7 @@ Our rationale is that this blanket `impl` would unnecessarily reject genuine use As illustration, the following is an example -```rust= +```rust trait BaseFunction { fn base_capability(&self); } @@ -547,7 +877,7 @@ where T: ManagementHandle Marker supertraits and supertraits with `default` items can be easily overlooked when users write subtrait implementations. They would register too little signal for the reader to recognise the significance of traits of these kinds. For this reason, we bias towards asking users to provide clear syntatical signals through `auto impl MarkerTrait` or `extern impl MarkerTrait`, so that the automatic derivation of such traits is easily recognisable and provides obvious site for documentation in case justification is waranted. -```rs= +```rust // It is almost always a good idea to explain // why a trait like below is implemented on a type. trait MarkerTrait {} @@ -564,9 +894,9 @@ impl Supertrait for MyType { } ``` -### What about checking whether another impl exists? +### What about checking whether another impl exists to decide automatic `impl`-filling? -We could potentially determine whether the opt-out is used based on whether an impl of the super trait exists, but we prefer not to. We have existing mechanism to determine the specialisation and implementation overlaps. Whether a supertrait `impl` overlaps or not, is not the concern of this proposal. +We could potentially determine whether the opt-out is used based on whether an `impl` of the supertrait exists, but we prefer not to. We have existing mechanism to determine the specialisation and implementation overlaps. Whether a supertrait `impl` overlaps or not, is not the concern of this proposal. If we would deduce whether an `auto impl` should be effected, there could present a hazard that silently changes the program behaviour. @@ -577,6 +907,10 @@ trait AutoMyTrait: MyTrait { auto impl MyTrait; } +trait MyOtherTrait { .. } + +struct Foo; + // Suppose we allow users to elide the `auto impl`/`extern impl` directive // and we deduce based on some applicability criterion ... impl AutoMyTrait for Foo {} // <-- generates MyTrait @@ -584,7 +918,11 @@ impl AutoMyTrait for Foo {} // <-- generates MyTrait // Suppose this item is added at one point of time impl MyTrait for T { .. } -impl MyOtherTrait for Foo {} // <-- now it should not be generated anymore +impl MyOtherTrait for Foo {} + +// QUESTION: which `impl MyTrait for Foo` fulfills the boud `Foo: MyTrait`? +// Should it be the `auto impl MyTrait;` with default items? +// Should it be the `impl MyTrait for T` with `T := Foo`? ``` ## Why is a hard error on ambiguity acceptable to us? @@ -616,9 +954,7 @@ In fact, this proposal is an improved version over this scheme. Previously, to d # Unresolved questions [unresolved-questions]: #unresolved-questions -- What parts of the design do you expect to resolve through the RFC process before this gets merged? -- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? -- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? +None so far. # Future possibilities [future-possibilities]: #future-possibilities From 67953681a8e46df1c524c3375059ae3588c76fc1 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Thu, 25 Sep 2025 00:41:58 +0800 Subject: [PATCH 03/14] do not unsafe the probe_extern_impl attribute; fix rendering and typos --- text/0000-supertrait-auto-impls.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/text/0000-supertrait-auto-impls.md b/text/0000-supertrait-auto-impls.md index 0dc63955236..e1ed4861359 100644 --- a/text/0000-supertrait-auto-impls.md +++ b/text/0000-supertrait-auto-impls.md @@ -243,7 +243,7 @@ impl SerializeByProxy for MyStruct { where Self: 'a; fn serialize(&self) -> MyStructProxy<'_> { - Proxy { + MyStructProxy { name: &self.name, digit: self.int % 10, tens: self.int / 10, @@ -373,16 +373,13 @@ This kind of code where `SmartPtr` *sometimes* implements `Deref` but *always* i ## Tenets - Backward-compatibility - - - We need to maintain that all the current `impl` blocks to compile, specifically the `impl Deref` litmus test mentioned in the [deref-receiver] motivating example. - This demand also extends to other possible library traits that may see relocation of items into a future supertrait, while ensuring that the existing `impl` blocks continue to compile. - Intuitional readability and clear syntatical signal - - - We strive for a syntax that is intuitional and easily connected to the existing constructs. - A successful design is one that enables a user to easily build a correct mental picture of the trait `impl`s with assistance from the syntatical features. - By extension, supertrait `impl`s should appear clearly in connection with subtrait `impl`s. - Flexibility - - - We strive for providing users the means to refactor their traits without compromising the expressiveness of the trait relationships. # Guide-level explanation @@ -741,7 +738,7 @@ For the following definition, a **non-marker** trait is a trait with an item, wh | Supertrait kind | `auto impl` block in sub-`trait` block | `auto impl` statement in sub-`trait` block | |-|-|-| | non-marker | Mandatory[a](#e-i-a) | Optional[b](#e-i-b) | -| marker Mandatory[c](#e-i-c) | +| marker | Mandatory[c](#e-i-c) | Mandatory[c](#e-i-c) | ### Case a: `auto impl` block of a non-marker supertrait in sub-`trait` block For illustration, here is an example. @@ -767,11 +764,11 @@ impl Subtrait for MyStruct { 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. +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. ```rust trait Ord: PartialOrd { - #[unsafe(probe_extern_impl)] + #[probe_extern_impl] auto impl PartialOrd { // ... } @@ -920,7 +917,7 @@ impl MyTrait for T { .. } impl MyOtherTrait for Foo {} -// QUESTION: which `impl MyTrait for Foo` fulfills the boud `Foo: MyTrait`? +// QUESTION: which `impl MyTrait for Foo` fulfills the bound `Foo: MyTrait`? // Should it be the `auto impl MyTrait;` with default items? // Should it be the `impl MyTrait for T` with `T := Foo`? ``` From 19a6df5d0cc2ec811aacfe605f30df2cf9bdcffa Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Thu, 25 Sep 2025 00:42:53 +0800 Subject: [PATCH 04/14] rename as per RFC number --- ...000-supertrait-auto-impls.md => 3851-supertrait-auto-impls.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-supertrait-auto-impls.md => 3851-supertrait-auto-impls.md} (100%) diff --git a/text/0000-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md similarity index 100% rename from text/0000-supertrait-auto-impls.md rename to text/3851-supertrait-auto-impls.md From d57c153303a9dc698bf5dde38ab5a1dfc807df50 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Mon, 29 Sep 2025 06:14:49 +0800 Subject: [PATCH 05/14] remove unsafe attribute; fix typo --- text/3851-supertrait-auto-impls.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index e1ed4861359..b61eae55712 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -731,7 +731,7 @@ If the sub-trait defines an item of the same name as an item in the super-trait, 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 +## Mandatory `extern impl` declaration For the following definition, a **non-marker** trait is a trait with an item, which can be `default` or not. A marker trait has zero associated items. @@ -763,8 +763,8 @@ impl Subtrait for MyStruct { ``` 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. +#### Extension: Possible relaxation through an 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 attribute `#[probe_extern_impl]` and apply further trait selection to decide whether the default implementation given by the `auto impl` block should be used. ```rust trait Ord: PartialOrd { From dd4adc350795abfae104493f673fd7ac2c2b7ee6 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Thu, 9 Oct 2025 08:29:01 +0000 Subject: [PATCH 06/14] additional motivation with Cow; semver consideration --- text/3851-supertrait-auto-impls.md | 69 ++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index b61eae55712..b45bbfd0710 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -1,6 +1,6 @@ - Feature Name: `supertrait_auto_impl` - Start Date: 2025-08-26 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/3851) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary @@ -199,6 +199,67 @@ impl Ord for X { } ``` +### Example: A possible [`ToOwned`](https://doc.rust-lang.org/stable/std/borrow/trait.ToOwned.html) refactor + +As of writing, `ToOwned` is defined as follows. +```rust +pub trait ToOwned { + type Owned: Borrow; + + fn to_owned(&self) -> Self::Owned; + + fn clone_into(&self, target: &mut Self::Owned) { ... } +} +``` + +`ToOwned` is a trait that could be further refined into the `AsOwned` concept and itself. + +```rust +pub trait AsOwned { + type Owned: Borrow; +} + +pub trait ToOwned: AsOwned { + auto impl AsOwned; + + fn to_owned(&self) -> Self::Owned; + + fn clone_into(&self, target: &mut Self::Owned) { + // same implementation + } +} +``` + +The appeal of this refinement is that there is a proper separation between the capability to *borrow out data* and that to *take and own data*. This enables `Cow<'_, _>` to be implemented in the following snippet. + +```rust +// Note that we do not out right require that the data can be taken and owned ... +pub enum Cow<'a, B: AsOwned + ?Sized> { + Borrowed(&'a B), + Owned(B::Owned), +} + +impl Deref for Cow<'_, B> { + type Target = B; + fn deref(&self) -> &Self::Target { + match self { + Self::Borrowed(v) => v, + Self::Owned(v) => v.borrow(), + } + } +} + +impl Cow<'_, B> { + // ... until the need arises here. + pub fn into_owned(self) -> B::Owned { + match self { + Self::Borrowed(v) => v.to_owned(), + Self::Owned(v) => v, + } + } +} +``` + ## Helpers for implementing traits This is not the main motivation for this RFC, but it is a secondary additional feature that this RFC enables. @@ -932,9 +993,11 @@ In any case, `auto impl Trait { .. }` blocks still remains available for cases w It is still up for discussion. -## Why use `unsafe` in this way +## What is the SemVer implication? + +A conservative calibration is, introducing `auto impl` directive into `trait Trait` definition is a major version breaking change, even though this feature intends to reduce rewrites in downstream crates and possibly no rewrite is required. The reason is that it is most probably a sign of trait refinement so that trait bounds could have been evolved. It is especially true as associated type projections or paths to associated method might need to be refactored: `BigTrait::Type` item is now moved into `SmallTrait::Type` and it is not always clear if `T::Type` would definitely resolve to `SmallTrait::Type` or there would exist ambiguity because `BigTrait` may have other, possibly new, supertraits which might also contain a `Type` associated item. This already warrants a major version bump. -As "default" supertrait implementation, _not in specialisation sense_, can be supplied in the subtrait definition, such implementation could be essential for the safe use of the subtrait `impl`. We should enable the subtrait authors to ensure that the safety invariants encoded in those "default" supertrait implementation are also observed, if the downstream implementor decides to supply its own supertrait `impl` instead of automatic derivation. +The case of marker traits is easier. In the most conservative case, no trait facts are changed and a downstream crate only needs to decide whether `auto impl` or `extern impl` suits the best. Semver stability then only relies on, in case of adoption of `extern impl`, whether the trait bound of the marker supertrait `impl` has changed. # Prior art [prior-art]: #prior-art From cdfd1da1a2535b16f5e3db852e5aec54f6dea8e1 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Fri, 21 Nov 2025 11:40:04 +0000 Subject: [PATCH 07/14] November edition - Clarify the rule on possible nesting - Clarify the semver hazards - Clarify the name resolution ambiguity from two supertraits Signed-off-by: Xiangfei Ding --- text/3851-supertrait-auto-impls.md | 68 ++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index b45bbfd0710..672a78bec17 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -698,7 +698,7 @@ Impl blocks using auto implementations are simply a short-hand for multiple impl 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: * Both impl blocks have the exact same set of generic items and where clauses, except that in the super trait any generic parameters that are unused by the super trait's `auto impl` are omitted. -* The equivalent impl block for the super trait contains the items in the `impl` block that come from the super trait, plus any items specified in the block of the `auto impl` (if any). In case of duplicates, the item from the `impl` block is preferred. +* The equivalent impl block for the super trait contains the items in the `impl` block that come from the super trait, plus any items specified in the block of the `auto impl` if any. In case of duplicates, the item from the `impl` block is preferred. * The equivalent impl block for the sub-trait contains any remaining items in the original `impl` block, plus an `extern impl` declaration. So for example, given these traits: @@ -788,9 +788,40 @@ If the super trait is `unsafe`, then the `auto impl` declaration must also be `u ## Naming ambiguity -If the sub-trait defines an item of the same name as an item in the super-trait, then the `auto impl` block must provide a default implementation of the item from the super trait. +If the sub-trait defines an item of the same name as an item in the super-trait, then the `auto impl` block must provide an implementation of that item from the super trait. -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`. +In this scenario, any item in an impl block of the sub-trait using the ambiguous name will always be resolved 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` or an overriding `auto impl` block inside the sub-trait `impl` block. + +If the sub-trait definition contains two `auto impl` directives and a sub-trait implementation has an item with a name that can be resolved to an associated item in both of the `auto impl` supertraits, irrespective of the associated item kind, then it **must** also be rejected as ambiguity. Either an `extern impl` statement or an overriding `auto impl` block is required for supplying an alternative definition of this item for each relevant supertrait. + +## Nesting `auto impl` in sub-trait defintion + +Nesting `auto impl` is allowed in a sub-trait definition or implementor. + +In a sub-trait definition site, only `auto impl`s is ever allowed in any level of nesting. If a target supertrait has at least one associated item or `auto impl` directive, **either** the full list of associated items and full list of `auto impl`s with concrete implementation are supplied as a default implementation at one nesting level, **or** the `auto impl` implementation is elided and the nesting terminates at this supertrait. + +```rust +trait Supersupertrait { + type Type; +} +trait Supertrait: Supersupertrait { + auto impl Supersupertrait; +} +trait Subtrait: Supertrait { + // A full implementation as default is required at each nesting level, or ... + auto impl Supertrait { + auto impl Supersupertrait { + type Type = (); + } + } +} +trait Subtrait2: Supertrait { + // No default implementation is supplied and the nesting terminates at this supertrait + auto impl Supertrait; +} +``` + +In a sub-trait implementor site, both `auto impl`s and `extern impl`s are allowed. ## Mandatory `extern impl` declaration @@ -886,6 +917,31 @@ impl Subtrait for MyStruct { ``` The reason for this is that `Supertrait` as a marker trait has no associated items. As we could not decide if the `Supertrait` would be implemented within the bounds attached to the `impl Subtrait` block, due to lack of syntatical signals, it is better to require explicit confirmation from the implementor on the condition of the marker trait `Supertrait` when this marker is applicable to `MyStruct`. +## SemVer consideration + +In this section we consider the impact on semantic versioning when a change to trait definition and implementors affects a `auto impl` syntax structure. + +### Addition of `auto impl` in sub-trait definition + +This is a SemVer hazard and can constitute a major change to public API, provided that the supertrait relation has not been changed. Implementers now have the obligation to ensure that their external implementation does not conflict with a potential default implementation at the sub-trait definition site. + +### Removal of `auto impl` in sub-trait definition + +This is a SemVer hazard and can constitute a major change. This requires the downstream implementors of the sub-trait to move the `auto impl` out of the `impl` block. + +### Addition and removal of `unsafe` qualifier on the `auto impl` directives + +This is a SemVer hazard and mandates a major change. The implementors should inspect their implementation against the trait safety specification and add or remove safety comments accordingly. It is possible that the semantics of the API would change as the safety obligation can propagate through the API across multiple crate boundaries. + +### Switching between `extern impl Supertrait` and `auto impl Supertrait` + +This is a SemVer hazard and mandates a minor change. Provided that both the sub-trait and the super-trait remains SemVer stable, this constitutes only a change in implementation detail. + +### Change in the proper defintion of super- and sub-traits + +This is a SemVer hazard and mandates a major change. The justification follows API change in trait irregardless of super- or sub-trait relationship. This scenario encompasses any changes in types, function signature, bounds, names. + + --- # Drawbacks @@ -993,12 +1049,6 @@ In any case, `auto impl Trait { .. }` blocks still remains available for cases w It is still up for discussion. -## What is the SemVer implication? - -A conservative calibration is, introducing `auto impl` directive into `trait Trait` definition is a major version breaking change, even though this feature intends to reduce rewrites in downstream crates and possibly no rewrite is required. The reason is that it is most probably a sign of trait refinement so that trait bounds could have been evolved. It is especially true as associated type projections or paths to associated method might need to be refactored: `BigTrait::Type` item is now moved into `SmallTrait::Type` and it is not always clear if `T::Type` would definitely resolve to `SmallTrait::Type` or there would exist ambiguity because `BigTrait` may have other, possibly new, supertraits which might also contain a `Type` associated item. This already warrants a major version bump. - -The case of marker traits is easier. In the most conservative case, no trait facts are changed and a downstream crate only needs to decide whether `auto impl` or `extern impl` suits the best. Semver stability then only relies on, in case of adoption of `extern impl`, whether the trait bound of the marker supertrait `impl` has changed. - # Prior art [prior-art]: #prior-art From 0ed09054f036d3f684a89aaf33fe30d6b3e3e1d7 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Fri, 21 Nov 2025 11:46:58 +0000 Subject: [PATCH 08/14] Mention the tower::Service trait for motivation Signed-off-by: Xiangfei Ding --- text/3851-supertrait-auto-impls.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index 672a78bec17..06c46ce8d66 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -92,7 +92,7 @@ where ``` ### Example: relaxed bounds via new supertraits -A common use case of supertraits is weaken bounds involved in associated items. There are occassions that a weakend supertrait could be useful. Suppose that we have a factory trait in the following example. In this example, the `async fn make` factory method could be weakened so that the future returned could be used in the context where the future is not required to be of `Send`. This has been enabled through the use of [the `trait_variant` crate](https://docs.rs/trait-variant/latest/trait_variant/). +A common use case of supertraits is weaken bounds involved in associated items. There are occassions that a weakend supertrait could be useful. Suppose that we have a factory trait in the following example. In this example, the `async fn make` factory method could be weakened so that the future returned could be used in the context where the future is not required to be of `Send`. This has been enabled through the use of [the `trait_variant` crate](https://docs.rs/trait-variant/latest/trait_variant/). The [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) trait would benefit greatly from this proposal by having also the `!Send` bound for local service without major refactoring. ```rust #[trait_variant::make(IntFactory: Send)] From 79aa0e89eb266ef311c58a2d36cd186c78ce0671 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Tue, 2 Dec 2025 18:18:44 +0000 Subject: [PATCH 09/14] extension: lifetime generic auto impl Signed-off-by: Xiangfei Ding --- text/3851-supertrait-auto-impls.md | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index 06c46ce8d66..b97adae0cdd 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -775,6 +775,39 @@ trait Subtrait: Supertrait { } ``` +### Extension: `auto impl` support for higher-kinded superbound + +Today a higher-kinded superbound is allowed as a superbound as long as only lifetime parameters are +used. + +```rust +trait Supertrait<'a> {} +trait Subtrait: for<'a> Supertrait<'a> {} +``` + +We propose to extend `auto impl` support to superbounds like this. In order to achieve this, +the `auto impl` item in traits and `impl` blocks are equipped with exclusively lifetime generic +parameters. + +```rust +trait Supertrait<'a> {} +trait Subtrait: for<'a> Supertrait<'a> { + auto impl<'a> Supertrait<'a>; +} +``` + +The usual no-shadowing rule applies when it comes to lifetime parameters. + +```rust +trait Subtrait<'a>: for<'a> Supertrait<'a> { + // ~~ first declared here + auto impl<'a> Supertrait<'a> { + //~^ ERROR lifetime name `'a` shadows a lifetime name that is already in scope + //~| lifetime `'a` already in scope + } +} +``` + ## Unsafe auto implementations The `auto impl` items can be marked `unsafe`, which declares that implementing the sub-trait without using the auto implementation is unsafe. From ed14188d4f62fd5e605e6e58c2ae5fb02be09c03 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Wed, 28 Jan 2026 22:46:07 +0000 Subject: [PATCH 10/14] apply suggestions; add one more refactor example Signed-off-by: Xiangfei Ding --- text/3851-supertrait-auto-impls.md | 159 ++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 36 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index b97adae0cdd..3d9ab98b131 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -1,10 +1,9 @@ - Feature Name: `supertrait_auto_impl` - Start Date: 2025-08-26 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/3851) -- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/149556) # Summary -[summary]: #summary We would like to allow nested trait `impl` blocks in `trait` defintion blocks and `impl Trait for` blocks, so that users can supply supertrait items in subtrait contexts. @@ -36,7 +35,6 @@ impl Subtrait2 for MyType { ``` # Motivation -[motivation]: #motivation ## Trait evolution @@ -44,11 +42,49 @@ Trait evolution is a treatment to existing trait hierarchy in a library. Difficu This RFC promises to improve the situation around trait evolution. It captures the common cases under this theme and aims to reduce rewrites in downstream crates, should the need to re-organise trait hierarchy arises. +### Example: trait refinement by forming trait hierarchy + +One long-standing issue with `std::fmt::{Read, Write}` traits is that their method signatures involve a type `std::fmt::Error` that is only applicable to `std` environment. There has been growing interest in making these traits available in the `#[no_std]` environment. + +The most promising refactoring could be introduction of a `#[no_std]` counterpart of the `Write` trait as an example. In doing so, the `Error` type could be specified with a custom error type that is suitable in the context. + +```rust +pub trait WriteFmt { + type Error; + fn write_str(&mut self, s: &str) -> Result<(), Self::Error>; +} +``` + +The original trait would need to be reworked and ideally in a way that does not involve a massive ecosystem-wide rewrite by fixing the `Error` type to `std::fmt::Error` and absolving the requirement on existing downstream implementations to supply the same supertrait implementation all over again. + +```rust +// In `std` crate ... +pub trait Write: WriteFmt { + // This shadows the supertrait method with one that hardcodes the error type. + fn write_str(&mut self, s: &str) -> Result<(), Error>; + + // ... provided methods elided ... + + auto impl WriteFmt { + // This is kind of silly, but it gives us the ability to change an + // existing impl from fmt::Error to !. + type Error = impl Into; + + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + Write::write_str(self, s) + } + } +} +``` + +The same argument applies to `std::io::{Read, Write}` traits as well. + ### Example: trait refinement by item hoisting into supertraits -As library code grows, there is frequently a need to breakdown a big trait into several smaller trait. This would have been a breaking change in view of SemVer and a user code rewrite is mandatory. However, the aim of this RFC is to ease the transition of downstream trait implementors to the new trait hierarchy by reducing the rewrites. +As library code grows, there is frequently a need to break down a big trait into several smaller traits. This would have been a breaking change in view of SemVer and a user code rewrite is mandatory. However, the aim of this RFC is to ease the transition of downstream trait implementors to the new trait hierarchy by reducing or eliminating the rewrites. Suppose that we start with a big trait `Subtrait` and it becomes desirable that `candidate_for_hoisting` method is hoisted into another trait. + ```rust trait Subtrait { fn candidate_for_hoisting(&self); @@ -66,7 +102,9 @@ where x.candidate_for_hoisting() } ``` + With this RFC, it is possible for the library author to perform the following refactor. + ```rust trait Supertrait { fn candidate_for_hoisting(&self); // <~ hoisted @@ -92,6 +130,7 @@ where ``` ### Example: relaxed bounds via new supertraits + A common use case of supertraits is weaken bounds involved in associated items. There are occassions that a weakend supertrait could be useful. Suppose that we have a factory trait in the following example. In this example, the `async fn make` factory method could be weakened so that the future returned could be used in the context where the future is not required to be of `Send`. This has been enabled through the use of [the `trait_variant` crate](https://docs.rs/trait-variant/latest/trait_variant/). The [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) trait would benefit greatly from this proposal by having also the `!Send` bound for local service without major refactoring. ```rust @@ -112,6 +151,7 @@ trait IntFactory: Send { ``` This RFC enables one to construct the trait in the following fashion. + ```rust trait LocalIntFactory { async fn make(&self) -> i32; @@ -139,6 +179,7 @@ trait IntFactory: Send { ### Example: automatic supertrait implementation A second prominent example is the `PartialOrd` and `Ord` traits. + ```rust trait PartialOrd { fn partial_cmp(&self, other: &Rhs) -> Option; @@ -163,6 +204,7 @@ impl Ord for X { ``` The `PartialOrd` trait could be reworked in this proposal as follows. + ```rust trait PartialOrd { fn partial_cmp(&self, other: &Rhs) -> Option; @@ -176,8 +218,11 @@ trait Ord: PartialOrd { } } ``` + There are now two choices for type `X` in the downstream crate. + - Delete the `impl PartialOrd for X`. Without the overlapping `impl`, the `auto impl` can stand in and take effect. + ```rust // delete: impl PartialOrd for X { .. } impl Ord for X { @@ -186,7 +231,9 @@ impl Ord for X { } } ``` + - Declare use of the existing applicable `impl PartialOrd for X`. + ```rust impl PartialOrd for X { // this is the same implementation @@ -202,6 +249,7 @@ impl Ord for X { ### Example: A possible [`ToOwned`](https://doc.rust-lang.org/stable/std/borrow/trait.ToOwned.html) refactor As of writing, `ToOwned` is defined as follows. + ```rust pub trait ToOwned { type Owned: Borrow; @@ -269,6 +317,7 @@ For some traits, it's difficult to implement the trait directly because the "raw ### Example: Serde The serde traits are notoriously difficult to implement directly. It's almost always done by macro. Imagine if you could write this: + ```rs struct MyStruct { name: String, @@ -314,23 +363,23 @@ impl SerializeByProxy for MyStruct { // via a proxy. } ``` + And then `MyStruct` automatically implements `Serialize` by creating a `MyStructProxy` instance and serializing the proxy. So for example `MyStruct { name: "a", int: 42 }` is serialized into json as `{"name":"a","tens":4,"digit":2}`. Right now, the only way to provide a helper like the one above is to either: -* Implement a proxy that emits the `Serialize` impl block, or -* Provide helper methods and instruct the user how to manually implement `Serialize` using the helpers you provided. - - +- Implement a proxy that emits the `Serialize` impl block, or +- Provide helper methods and instruct the user how to manually implement `Serialize` using the helpers you provided.
Hypothetical example: Evolving the `Deref` trait - + This section is included because whether Deref is to be merged with Receiver is up for deliberation at the moment. [deref-receiver]: #Deref-Receiver-evolution As part of the `arbitrary_self_types` feature, we need to split `Deref` into two traits. Right now, the `Deref` trait looks like this: + ```rs pub trait Deref { type Target: ?Sized; @@ -338,7 +387,9 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } ``` + But we need it to look like this: + ```rs pub trait Receiver { type Target: ?Sized; @@ -348,7 +399,9 @@ pub trait Deref: Receiver { fn deref(&self) -> &Self::Target; } ``` + However, making this change is difficult due to backwards compatibility. There are many crates in the ecosystem with code that looks like this: + ```rs struct MyStruct(u8); @@ -359,14 +412,17 @@ impl Deref for MyStruct { } } ``` + or like this: + ```rs fn assert_deref() where MyStruct: Deref {} ``` -The feature in this RFC provides a mechanism for _trait evolution_ of `Deref` and `Receiver` where it becomes possible to split a trait into a super-trait and sub-trait without breaking backwards compatibility in downstream crates. + +The feature in this RFC provides a mechanism for *trait evolution* of `Deref` and `Receiver` where it becomes possible to split a trait into a super-trait and sub-trait without breaking backwards compatibility in downstream crates. ```rust pub trait Receiver { @@ -400,6 +456,7 @@ where ### Why not a blanket impl? Unfortunately, this does not work: + ```rs pub trait Receiver { type Target: ?Sized; @@ -414,7 +471,9 @@ impl Receiver for T { type Target = ::Target; } ``` + The problem is that crates need to be able to write this code: + ```rs struct SmartPtr(*mut T); @@ -428,29 +487,30 @@ impl Deref for SmartPtr { } } ``` + This kind of code where `SmartPtr` *sometimes* implements `Deref` but *always* implements `Receiver` is not possible with a blanket implementation. The feature proposed by this RFC would make the above construction a possibility.
- + ## Tenets - Backward-compatibility - - We need to maintain that all the current `impl` blocks to compile, specifically the `impl Deref` litmus test mentioned in the [deref-receiver] motivating example. - - This demand also extends to other possible library traits that may see relocation of items into a future supertrait, while ensuring that the existing `impl` blocks continue to compile. + - We need to maintain that all the current `impl` blocks to compile, specifically the `impl Deref` litmus test mentioned in the [deref-receiver] motivating example. + - This demand also extends to other possible library traits that may see relocation of items into a future supertrait, while ensuring that the existing `impl` blocks continue to compile. - Intuitional readability and clear syntatical signal - - We strive for a syntax that is intuitional and easily connected to the existing constructs. - - A successful design is one that enables a user to easily build a correct mental picture of the trait `impl`s with assistance from the syntatical features. - - By extension, supertrait `impl`s should appear clearly in connection with subtrait `impl`s. + - We strive for a syntax that is intuitional and easily connected to the existing constructs. + - A successful design is one that enables a user to easily build a correct mental picture of the trait `impl`s with assistance from the syntatical features. + - By extension, supertrait `impl`s should appear clearly in connection with subtrait `impl`s. - Flexibility - - We strive for providing users the means to refactor their traits without compromising the expressiveness of the trait relationships. + - We strive for providing users the means to refactor their traits without compromising the expressiveness of the trait relationships. # Guide-level explanation -[guide-level-explanation]: #guide-level-explanation This section gives an overview of the `auto impl` feature and some example use-cases. ## Overview It is possible to declare that implementations of a sub-trait should automatically implement a given supertrait. + ```rs trait MyTrait { fn my_func(&self); @@ -462,7 +522,9 @@ trait MySubTrait: MyTrait { fn my_second_func(&self); } ``` + Given the above traits, when you implement `MySubTrait`, you must also specify items from `MyTrait`. + ```rs impl MySubTrait for String { fn my_func(&self) { @@ -474,7 +536,9 @@ impl MySubTrait for String { } } ``` + In the above case, it is an error to not specify all items from `MyTrait`. However, it is possible to opt-out of implementing `MyTrait` in the `impl MySubTrait` block: + ```rs impl MyTrait for String { fn my_func(&self) { @@ -488,6 +552,7 @@ impl MySubTrait for String { } } ``` + The `extern impl MyTrait` declaration specifies the impl block does not automatically implement `MyTrait`, and that another impl block is used instead. ### Example: Trait evolution @@ -535,11 +600,10 @@ impl Subtrait for Middleware { } ``` - - ## Default auto implementations It is possible to provide default implementations of functions from the super trait. + ```rs trait MyTrait { fn my_func(&self); @@ -556,6 +620,7 @@ trait MySubTrait: MyTrait { fn my_second_func(&self); } ``` + In this case, an impl block for `MySubTrait` will still automatically implement `MyTrait`, but you are not required to provide an implementation of `my_func` since a default implementation exists. ### Example: Helpers for implementing a trait @@ -563,13 +628,16 @@ In this case, an impl block for `MySubTrait` will still automatically implement With default auto implementations, it becomes possible to provide a sub-trait whose purpose is to help you implement the super trait in a specific way. For example, given this super trait: + ```rs trait EventHandler { type Event; fn handle_event(&mut self, event: Self::Event); } ``` + Then you might have a helper for implementing `EventHandler` for a specific event type: + ```rs enum MouseEvent { ClickEvent(ClickEvent), @@ -592,7 +660,9 @@ trait MouseEventHandler: EventHandler { fn move_event(&mut self, event: MoveEvent); } ``` + This allows crates to implement `EventHandler` by writing this code: + ```rs struct PrintHandler; @@ -605,11 +675,13 @@ impl MouseEventHandler for PrintHandler { } } ``` + The `MouseEventHandler` trait could even come from a different crate than `EventHandler`. ## Unsafe auto impl It's possible to declare that an auto implementation is unsafe. + ```rs trait MySubTrait: MyTrait { unsafe auto impl MyTrait; @@ -617,7 +689,9 @@ trait MySubTrait: MyTrait { fn my_second_func(&self); } ``` + This means that it is unsafe to override the auto implementation. + ```rs impl MySubTrait for String { // unsafe is required here because the `auto impl` @@ -629,11 +703,13 @@ impl MySubTrait for String { } } ``` + If the super trait is unsafe, then the `auto impl` must also be unsafe. ### Example: Safely implement unsafe trait This can be used to provide a safe way to implement an unsafe trait. + ```rs /// Implementers must ensure that even() returns an /// even number. @@ -662,11 +738,10 @@ impl Double for String { --- - # Reference-level explanation -[reference-level-explanation]: #reference-level-explanation In a trait declaration, you may declare one or more `auto impl` items with a block that provides implementations for one or more items from the super trait. + ```rs trait SubTrait: SuperTrait { auto impl SuperTrait { @@ -678,17 +753,20 @@ trait SubTrait: SuperTrait { } } ``` + All items inside the `auto impl` block must match an item from the super trait of the same name and signature. When the block is empty, it is legal to use a semicolon instead. That is, these are equivalent: -* `auto impl SuperTrait {}` -* `auto impl SuperTrait;` +- `auto impl SuperTrait {}` +- `auto impl SuperTrait;` The type of the super trait can be anything that matches the [TypePath](https://doc.rust-lang.org/reference/paths.html#grammar-TypePath) grammar and evaluates to a trait. This means that, for example, this is legal: + ```rs trait SubTrait { auto impl MyGenericTrait; } ``` + The trait must be a super-trait. That is, the trait solver must be able to prove that `where T: SubTrait` implies `where T: SuperTrait`. ## Impl blocks @@ -697,9 +775,9 @@ Impl blocks using auto implementations are simply a short-hand for multiple impl 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: -* Both impl blocks have the exact same set of generic items and where clauses, except that in the super trait any generic parameters that are unused by the super trait's `auto impl` are omitted. -* The equivalent impl block for the super trait contains the items in the `impl` block that come from the super trait, plus any items specified in the block of the `auto impl` if any. In case of duplicates, the item from the `impl` block is preferred. -* The equivalent impl block for the sub-trait contains any remaining items in the original `impl` block, plus an `extern impl` declaration. +- Both impl blocks have the exact same set of generic items and where clauses, except that in the super trait any generic parameters that are unused by the super trait's `auto impl` are omitted. +- The equivalent impl block for the super trait contains the items in the `impl` block that come from the super trait, plus any items specified in the block of the `auto impl` if any. In case of duplicates, the item from the `impl` block is preferred. +- The equivalent impl block for the sub-trait contains any remaining items in the original `impl` block, plus an `extern impl` declaration. So for example, given these traits: @@ -716,7 +794,9 @@ trait SubTrait: SuperTrait { fn my_second_item(&self, arg: T); } ``` + then this impl block: + ```rs impl SubTrait for MyStruct where @@ -726,7 +806,9 @@ where fn my_second_item(&self, arg: T) {} } ``` + is short-hand for this: + ```rs // The original impl block MINUS the super-trait's methods // PLUS an extern impl statement: @@ -753,9 +835,11 @@ where fn my_default_item(&self, arg: T) {} } ``` + Note that this implies that you *must* use `extern impl` to provide your own implementation of the super trait. If you don't, then by the above rule, the generated impl for the super trait would overlap with your custom implementation, which is illegal by the standard trait rules. ### Extension: item aliasing in `auto impl` + To reduce possible verbosity, we can propose a future extension to `auto impl` default implementation block, in case supertrait items can be implemented as an alias to subtrait items. When a subtrait associated method has the same signature as a supertrait associated method in terms of generics, has a set of `where` bounds that satisfies the supertrait item `where` bounds and compatible function signature, the `auto impl` default implementation can be simplified into a assignment statement, instead of a complete function body with a delegation call. @@ -814,8 +898,8 @@ The `auto impl` items can be marked `unsafe`, which declares that implementing t When an `auto impl` is declared unsafe, then: -* To opt-out, you must write `unsafe extern impl`. -* If any methods from the `unsafe auto impl` block are overridden, then the `impl` block must be `unsafe`. +- To opt-out, you must write `unsafe extern impl`. +- If any methods from the `unsafe auto impl` block are overridden, then the `impl` block must be `unsafe`. If the super trait is `unsafe`, then the `auto impl` declaration must also be `unsafe`. @@ -866,7 +950,9 @@ For the following definition, a **non-marker** trait is a trait with an item, wh | marker | Mandatory[c](#e-i-c) | Mandatory[c](#e-i-c) | ### Case a: `auto impl` block of a non-marker supertrait in sub-`trait` block + For illustration, here is an example. + ```rust trait Supertrait { type Item; @@ -886,9 +972,11 @@ impl Subtrait for MyStruct { // ... the code will be rejected for overlapping `impl Supertrait`s } ``` + 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 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 attribute `#[probe_extern_impl]` and apply further trait selection to decide whether the default implementation given by the `auto impl` block should be used. ```rust @@ -911,7 +999,9 @@ impl Ord for MyType { However, this practice will not be encouraged eventually under provision of this RFC. For this reason, we also propose a future-compatibility lint, which will be escalated on a future Edition boundary to denial. The lint shall highlight the existing `auto impl` block in the subtrait definition and suggest an explicit `extern impl` statement in the subtrait implementation. ### Case b: `auto impl` block of a non-marker supertrait in sub-`trait` statement + For illustration, here is an example. + ```rust trait Supertrait { type Item; @@ -928,12 +1018,15 @@ impl Subtrait for MyStruct { extern impl Supertrait; } ``` + The reason for this is that `trait Subtrait` has not already provided its implementation, an implementation of `Subtrait` must supply an implementation of `Supertrait`, which could have existed before introducing the `auto impl Supertrait`. If `auto impl` statement is declared on a non-marker supertrait without a default implementation, the `extern impl` is optional so that we do not penalise the existing trait implementors. ### Case c: `auto impl` block of a marker supertrait + For illustration, here is an example. + ```rust trait Supertrait {} trait Subtrait: Supertrait { @@ -948,6 +1041,7 @@ impl Subtrait for MyStruct { extern impl Supertrait; } ``` + The reason for this is that `Supertrait` as a marker trait has no associated items. As we could not decide if the `Supertrait` would be implemented within the bounds attached to the `impl Subtrait` block, due to lack of syntatical signals, it is better to require explicit confirmation from the implementor on the condition of the marker trait `Supertrait` when this marker is applicable to `MyStruct`. ## SemVer consideration @@ -974,16 +1068,13 @@ This is a SemVer hazard and mandates a minor change. Provided that both the sub- This is a SemVer hazard and mandates a major change. The justification follows API change in trait irregardless of super- or sub-trait relationship. This scenario encompasses any changes in types, function signature, bounds, names. - --- # Drawbacks -[drawbacks]: #drawbacks - This is yet another new language syntax to teach. # Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives ## A `auto impl` in a `trait` definition block should not be interpreted as a blanket `impl` @@ -1083,8 +1174,6 @@ In any case, `auto impl Trait { .. }` blocks still remains available for cases w It is still up for discussion. # Prior art -[prior-art]: #prior-art - ## Implementable trait-alias @@ -1095,12 +1184,10 @@ It was suggested in the [RFC 3437](https://github.com/rust-lang/rfcs/pull/3437) In fact, this proposal is an improved version over this scheme. Previously, to disambiguate names from different supertrait namespaces, one appends the associated item identifies with a qualification and generic arguments when necessary. However, the old proposal would apply more deduction, by the compiler, on whether supertrait `impl`s are demanded and it is a weaker response to the tenet that prefers more syntatical signals to compiler deduction. # Unresolved questions -[unresolved-questions]: #unresolved-questions None so far. # Future possibilities -[future-possibilities]: #future-possibilities Think about what the natural extension and evolution of your proposal would be and how it would affect the language and project as a whole in a holistic From 0c2ac3421b007b1914e61372010a301243b8a12d Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Sat, 14 Feb 2026 14:29:32 +0000 Subject: [PATCH 11/14] Resolve ambiguous wording on name collision with supertraits --- text/3851-supertrait-auto-impls.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index 3d9ab98b131..dd93d9ca288 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -909,7 +909,13 @@ If the sub-trait defines an item of the same name as an item in the super-trait, In this scenario, any item in an impl block of the sub-trait using the ambiguous name will always be resolved 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` or an overriding `auto impl` block inside the sub-trait `impl` block. -If the sub-trait definition contains two `auto impl` directives and a sub-trait implementation has an item with a name that can be resolved to an associated item in both of the `auto impl` supertraits, irrespective of the associated item kind, then it **must** also be rejected as ambiguity. Either an `extern impl` statement or an overriding `auto impl` block is required for supplying an alternative definition of this item for each relevant supertrait. +If the sub-trait definition contains two `auto impl` directives and a sub-trait implementation has an item with a name that ... + +- can be resolved to an associated item in both of the `auto impl` supertraits, +- but cannot be resolved to an associated item in the sub-trait trait definition, +then, irrespective of the associated item kind, the item **must** also be rejected as ambiguity. Either an `extern impl` statement or an overriding `auto impl` block is required for supplying an alternative definition of this item for each relevant supertrait. + +On the contrary, when the sub-trait definition defines an item name and an `auto impl` whose supertriat also defines an item with a matching name, this name appearing in all implementations of this sub-trait will always resolve to the item associated to the sub-trait. ## Nesting `auto impl` in sub-trait defintion From 25597e7b584d355c4fb6fe170e26ee734e3d42ff Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Sat, 14 Feb 2026 20:51:44 +0000 Subject: [PATCH 12/14] Clarify the name ambiguity rules by providing a worked example --- text/3851-supertrait-auto-impls.md | 73 ++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index dd93d9ca288..60b1c95d6a0 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -38,7 +38,7 @@ impl Subtrait2 for MyType { ## Trait evolution -Trait evolution is a treatment to existing trait hierarchy in a library. Difficulty has arised in the past that hoisting items from the current trait into a new supertrait, or introduction of a second trait. +Trait evolution is a treatment to existing trait hierarchy in a library. Difficulty has arose in the past that hoisting items from the current trait into a new supertrait, or introduction of a second trait. This RFC promises to improve the situation around trait evolution. It captures the common cases under this theme and aims to reduce rewrites in downstream crates, should the need to re-organise trait hierarchy arises. @@ -131,7 +131,7 @@ where ### Example: relaxed bounds via new supertraits -A common use case of supertraits is weaken bounds involved in associated items. There are occassions that a weakend supertrait could be useful. Suppose that we have a factory trait in the following example. In this example, the `async fn make` factory method could be weakened so that the future returned could be used in the context where the future is not required to be of `Send`. This has been enabled through the use of [the `trait_variant` crate](https://docs.rs/trait-variant/latest/trait_variant/). The [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) trait would benefit greatly from this proposal by having also the `!Send` bound for local service without major refactoring. +A common use case of supertraits is weaken bounds involved in associated items. There are occasions that a weakened supertrait could be useful. Suppose that we have a factory trait in the following example. In this example, the `async fn make` factory method could be weakened so that the future returned could be used in the context where the future is not required to be of `Send`. This has been enabled through the use of [the `trait_variant` crate](https://docs.rs/trait-variant/latest/trait_variant/). The [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) trait would benefit greatly from this proposal by having also the `!Send` bound for local service without major refactoring. ```rust #[trait_variant::make(IntFactory: Send)] @@ -319,6 +319,12 @@ For some traits, it's difficult to implement the trait directly because the "raw The serde traits are notoriously difficult to implement directly. It's almost always done by macro. Imagine if you could write this: ```rs +// For reference, the Serialize trait is taken from a recent version of the serde crate. +pub trait Serialize { + fn serialize(&self, serializer: S) -> Result + where S: Serializer; +} + struct MyStruct { name: String, int: i32, @@ -557,7 +563,7 @@ The `extern impl MyTrait` declaration specifies the impl block does not automati ### Example: Trait evolution -Over time, needs have arised to establish a hierarchy of traits, so that the parts and pieces of existing "big" library traits, be it from `std` or ecosystem crates, can be extraced and pulled back into supertraits without requiring a breaking change in the downstream crates. In most cases, the assoication of methods to be "refactored" and the destination supertraits can be determined without ambiguity. This falls under a bigger theme of trait evolution, which concerns how a historically big trait can be broken down and refined into smaller traits and trait hierarchy. [RFC 1210](https://rust-lang.github.io/rfcs/1210-impl-specialization.html#the-default-keyword) provided an example of a trait evolution and how specialisation could have eased the refactoring. +Over time, needs have arose to establish a hierarchy of traits, so that the parts and pieces of existing "big" library traits, be it from `std` or ecosystem crates, can be extracted and pulled back into supertraits without requiring a breaking change in the downstream crates. In most cases, the association of methods, that are to be "refactored", and the destination supertraits can be determined without ambiguity. This falls under a bigger theme of trait evolution, which concerns how a historically big trait can be broken down and refined into smaller traits and trait hierarchy. [RFC 1210](https://rust-lang.github.io/rfcs/1210-impl-specialization.html#the-default-keyword) provided an example of a trait evolution and how specialisation could have eased the refactoring. With this proposal, specialisation is not required and, instead, the pulled-back supertrait implementation applies directly within the context of the subtrait implementation. @@ -913,9 +919,66 @@ If the sub-trait definition contains two `auto impl` directives and a sub-trait - can be resolved to an associated item in both of the `auto impl` supertraits, - but cannot be resolved to an associated item in the sub-trait trait definition, + then, irrespective of the associated item kind, the item **must** also be rejected as ambiguity. Either an `extern impl` statement or an overriding `auto impl` block is required for supplying an alternative definition of this item for each relevant supertrait. -On the contrary, when the sub-trait definition defines an item name and an `auto impl` whose supertriat also defines an item with a matching name, this name appearing in all implementations of this sub-trait will always resolve to the item associated to the sub-trait. +```rust +pub trait TraitA { + fn foo(); +} + +pub trait TraitB { + fn foo(); +} + +pub trait Subtrait: TraitA + TraitB { + auto impl TraitA; // OK + auto impl TraitB; // OK +} + +impl Subtrait for MyType { + fn foo(); //~ ERROR ambiguous associated item `foo` + // Reason: it could be resolved to either `TraitA::foo` or `TraitB::foo` + //~ ERROR missing `auto impl TraitA` or `extern impl TraitA` + //~^ ERROR missing `auto impl TraitB` or `extern impl TraitB` + // ... because `foo` is not used to discharge `auto impl TraitA` or `auto impl TraitB` +} + +let _ = ::foo(); //~ ERROR `Subtrait` has no associated method called `foo` +```` + +On the contrary, when the sub-trait definition defines an item name and an `auto impl` whose supertrait also defines an item with a matching name, this name appearing in all implementations of this sub-trait will always resolve to the item associated to the sub-trait. + +```rust +pub trait TraitA { + fn foo(); +} + +pub trait TraitB { + fn foo(); +} + +pub trait Subtrait: TraitA + TraitB { + auto impl TraitA; // OK + auto impl TraitB; // OK + fn foo(); // Also OK (*) +} + +// Now, `Subtrait::foo` always resolves to the definition (*) + +impl Subtrait for MyType { + auto impl TraitA { + fn foo() {} + } + auto impl TraitB { + fn foo() {} + } + fn foo() {} // (**) +} + +let _ = ::foo(); // OK, `Subtrait::foo` resolves to the instance (**) +// NOT `::foo` or `::foo` +``` ## Nesting `auto impl` in sub-trait defintion @@ -1070,7 +1133,7 @@ This is a SemVer hazard and mandates a major change. The implementors should ins This is a SemVer hazard and mandates a minor change. Provided that both the sub-trait and the super-trait remains SemVer stable, this constitutes only a change in implementation detail. -### Change in the proper defintion of super- and sub-traits +### Change in the proper definition of super- and sub-traits This is a SemVer hazard and mandates a major change. The justification follows API change in trait irregardless of super- or sub-trait relationship. This scenario encompasses any changes in types, function signature, bounds, names. From 56ca65e9e6d2a6e615ec91fa83f318cbda9666af Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Sat, 14 Feb 2026 21:08:09 +0000 Subject: [PATCH 13/14] Strengthen then ambiguity resolving wording In name resolution, we should only care about required items. --- text/3851-supertrait-auto-impls.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index 60b1c95d6a0..a1217bd08b7 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -911,13 +911,13 @@ If the super trait is `unsafe`, then the `auto impl` declaration must also be `u ## Naming ambiguity -If the sub-trait defines an item of the same name as an item in the super-trait, then the `auto impl` block must provide an implementation of that item from the super trait. +If the sub-trait defines an item of the same name as a *required* item in the super-trait, then an `auto impl` block for that supertrait will be implicated so that it can provide an implementation of that item from the supertrait. In this scenario, any item in an impl block of the sub-trait using the ambiguous name will always be resolved 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` or an overriding `auto impl` block inside the sub-trait `impl` block. If the sub-trait definition contains two `auto impl` directives and a sub-trait implementation has an item with a name that ... -- can be resolved to an associated item in both of the `auto impl` supertraits, +- can be resolved to a **required** associated item in both of the `auto impl` supertraits, - but cannot be resolved to an associated item in the sub-trait trait definition, then, irrespective of the associated item kind, the item **must** also be rejected as ambiguity. Either an `extern impl` statement or an overriding `auto impl` block is required for supplying an alternative definition of this item for each relevant supertrait. From cb81539163ed3d2292f0620fa72a6484cd3c8d74 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Mon, 16 Feb 2026 08:55:38 +0000 Subject: [PATCH 14/14] Refine wording around use of `unsafe` Signed-off-by: Xiangfei Ding --- text/3851-supertrait-auto-impls.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/text/3851-supertrait-auto-impls.md b/text/3851-supertrait-auto-impls.md index a1217bd08b7..2dc81c9b98b 100644 --- a/text/3851-supertrait-auto-impls.md +++ b/text/3851-supertrait-auto-impls.md @@ -502,8 +502,8 @@ This kind of code where `SmartPtr` *sometimes* implements `Deref` but *always* i - Backward-compatibility - We need to maintain that all the current `impl` blocks to compile, specifically the `impl Deref` litmus test mentioned in the [deref-receiver] motivating example. - This demand also extends to other possible library traits that may see relocation of items into a future supertrait, while ensuring that the existing `impl` blocks continue to compile. -- Intuitional readability and clear syntatical signal - - We strive for a syntax that is intuitional and easily connected to the existing constructs. +- Intuitive readability and clear syntatical signal + - We strive for a syntax that is Intuitive and easily connected to the existing constructs. - A successful design is one that enables a user to easily build a correct mental picture of the trait `impl`s with assistance from the syntatical features. - By extension, supertrait `impl`s should appear clearly in connection with subtrait `impl`s. - Flexibility @@ -686,9 +686,11 @@ The `MouseEventHandler` trait could even come from a different crate than `Event ## Unsafe auto impl -It's possible to declare that an auto implementation is unsafe. +When the supertrait is an `unsafe trait`, its auto implementation must also be `unsafe`. ```rs +unsafe trait MyTrait {} + trait MySubTrait: MyTrait { unsafe auto impl MyTrait; @@ -696,7 +698,7 @@ trait MySubTrait: MyTrait { } ``` -This means that it is unsafe to override the auto implementation. +This implies that it is unsafe to override the auto implementation. ```rs impl MySubTrait for String { @@ -900,14 +902,12 @@ trait Subtrait<'a>: for<'a> Supertrait<'a> { ## Unsafe auto implementations -The `auto impl` items can be marked `unsafe`, which declares that implementing the sub-trait without using the auto implementation is unsafe. - -When an `auto impl` is declared unsafe, then: +If the super trait is `unsafe`, then the `auto impl` declaration must also be `unsafe`. -- To opt-out, you must write `unsafe extern impl`. -- If any methods from the `unsafe auto impl` block are overridden, then the `impl` block must be `unsafe`. +When an `auto impl` is declared unsafe, then the choices are -If the super trait is `unsafe`, then the `auto impl` declaration must also be `unsafe`. +- To opt-out, and you must write `unsafe extern impl`. +- If any methods from the `unsafe auto impl` block are to be overridden, then the `auto impl` block must be `unsafe`. ## Naming ambiguity