RfL context
This enables us to move type Target
from trait Deref
into trait Receiver
without breaking existing impl Deref
blocks in Rust ecosystem.
This RFC concerns itself with enabling subtrait impl
blocks to supply associated items from one or several supertraits.
This RFC proposes an extension so that subtrait impl
blocks may designate the associated types from supertraits so that the respective supertraits impl
can be implied by collecting the required associated items, without resorting to individual, separate impl
-supertrait blocks.
Supertraits of a subtrait in this context is defined solely in the form of type bound list, as illustrated in Listing 1, as opposed to examples in Listing 2.
Supertrait Super1
and Super2
registered with the subtrait Sub
as type bounds
Trait Super1
and Super2
are not registered as supertraits of trait Sub
or Sub2
in these examples
impl
subtrait blocks todayThe way to introduce impl
of a subtrait now is rigid, in the sense that one impl
block for the subtrait and additional one each for the involved supertrait.
There are two possible problems arising from this typical case.
impl
s can refer to items from supertrait impl
s, but the supertrait impl
s can be located at other places, even in different modules of the crate. This can impact readability of the code, as readers need to jump extra loops to make the connections.impl
blocks can become prohibitive as the type signature of Self
, the where
bounds and the list of generics grow.The proposal is to allow users to declare supertrait associated items directly in the subtrait impl
blocks.
To build the intuition, Listing 3 could have been written into the following.
Note that the associated items type T
and fn super_call
must be resolved to that from the trait Super
.
However, this proposal also guarantee that Listing 3 on its own must continue to compile.
Rust impl
-trait blocks has always been self-containing today. With this proposal, this principle will remain upheld. For an impl
-subtrait block to be valid, when it comes to completeness, the following rules must be observed.
Super
is defined, so are all associated items from the trait Super
.Then in turn, the compiler should guarantee the following.
Super<..>
is defined, it has the same effect as a standalone impl<..> Super<..>
block, so that it can be considered that the type bound Self: Super<..>
is satisfied.The primary reason that a clash between a subtrait item with a supertrait item is not considered for ambiguity and the item would be resolved to the subtrait item, is due to a property that we would like to uphold, which is to allow the user to easily cut out the supertrait impl
block and add into the subtrait impl
block without qualifying the subtrait item. The compiler, in this case, should have sufficiently erred about the name clash due to pasting in the supertrait items.
Under this proposal, these are the notable valid examples.
Under this proposal, the following should not compile.
In case that there are clashes on associated item names among supertrait and subtrait items, attempting to declare one of them from supertraits without qualification must be rejected.
To resolve the ambiguity, one must introduce path qualification of the targeted trait in front of the associated item identifier. If the targeted subtrait or supertrait has generics, the generics should be included in the path.
Let us suppose Sub
trait has a supertrait Super1
, which in turn has a supertrait Super2
.
Then, this proposal enables the impl
on subtrait with various depths of declaration.
The following is a suggestion on possible compiler implementation.
We will now allow path elements and generics in the associated item location. As far as it would look like, introduction of grammar ambiguity is unlikely.
Given that identifiers can be foreign to the impl
, and the associated item resolution is eager, we would like to one extra phase. The exact work requires a quick survey and prototyping.
The work here is to make sure that the respective DefId
s of supertraits implied impl
s shall be procured. In this way, no changes to trait solving are required, which is very critical to the success of implementation of this language extension.
We will defer the span information, which is critical to diagnostics, to a later phase of the prototyping.
Why should we not do this?
Now unless the identifier names are very self-evident, the supertrait associated items may be seen too magical as they would appear very irrelevant to the impl
block.
One could get confused if one is not very familiar with the trait definition and when subsequently encouter a supertrait item that has not logical connection to the rest of the subtrait impl
block.
One can foresee that given this option, users may elect to flood a subtrait impl
block with all supertrait declaration. It has been a long appreciated tradition of Rust that code should be organised and partitioned by pragmatics and functions. There is a risk that the abuse of this language extension will break this nice property.
The author has not been made aware of prior discussion.
impl
blocks?