Also previously called #[inherent] impl
. If temperature is generally positive, I'll hopefully write up a full RFC.
Proposal:
Allow impl Trait for Type
blocks to be tagged as #[fundamental]
when it would be coherent for the containing crate to write an inherent impl for Type
. (If it would be an error to write impl Type
) This has the following effects:
- Communicates to the compiler and reader that this trait functionality is a fundamental part of the functionality offered by the receiver type. (e.g. types that exist to be used as an
Iterator
.) - Documentation features the trait implementation more prominently than other trait implementations. This plays a similar role to
doc(notable_trait)
, but tags the impl instead of the trait. The ⓘ for types with a#[fundamental] impl
should probably point out the tagged impls instead ofdoc(notable)
traits. - When performing method lookup for
Type
, always treatTrait
as in scope. That is,item.method()
should findTrait::method
even ifTrait
has not been imported into scope. - If
Trait::method
is ambiguous with another trait method which is in scope but for which the impl block is not marked#[fundamental]
, the method from the#[fundamental]
ly implemented trait is chosen. This applies even to default methods on the trait which are not present in the#[fundamental]
impl block. Inherent methods still have priority over all trait methods. - If
Type
defines an associated item which shadows an item ofTrait
, this is a warning.
That last point is what primarily differentiates this from prior concepts/proposals, where #[inherent]
would actually create an inherent impl with the trait items. Leaving it as a lint is for one specific reason: adding default/provided methods to a trait should remain backwards-compatible, and potentially causing a duplicate method error is decidedly not backwards-compatible. The lint could potentially be split into two groups on two axis: shadowing with the same or different signature is more/less likely to be intentional/nonproblematic, and shadowing for a locally defined trait is more likely to be resolvable than for an external crate.
This doesn't completely eliminate the desire for trait import preludes (e.g. trait impls for external types), but does cut their necessity somewhat. Consider that if this existed from day 0, Iterator
wouldn't have as strong of a need to be in the std prelude, since iterator types could tag their impl as #[fundamental]
to get the iterator methods available without the need of importing the type.