Repository: rust
Issue: Tracking Issue for attributes changing "Minimal Complete Definition" of a trait
Labels: A-trait-system,T-lang,C-tracking-issue

<!--
NOTE: For library features, please use the "Library Tracking Issue" template instead.

Thank you for creating a tracking issue! 📜 Tracking issues are for tracking a
feature from implementation to stabilisation. Make sure to include the relevant
RFC for the feature if it has one. Otherwise provide a short summary of the
feature and link any relevant PRs or issues, and remove any sections that are
not relevant to the feature.

Remember to add team labels to the tracking issue.
For a language team feature, this would e.g., be `T-lang`.
Such a feature should also be labeled with e.g., `F-my_feature`.
This label is used to associate issues (e.g., bugs and design questions) to the feature.
-->

<!-- This is a tracking issue for the RFC "XXX" (rust-lang/rfcs#NNN).
The feature gate for the issue is `#![feature(FFF)]`. -->

This is a tracking issue for attributes changing "Minimal Complete Definition" of a trait. "Minimal Complete Definition" is effectively a set of rules you need to follow (by implementing items of the trait) in order for a trait implementation to be "Complete" (and compile). Normally there is only one rule — implement all items that do not have defaults. However, sometimes it is meaningful to add additional restrictions.

As a simple example consider a trait with functions `equal` and `not_equal`, both of them can be implemented as `!the_other_function(...)`, __but only if the other one has a meaningful implementation__. Thus, it may be beneficial for the library design to make Minimal Complete Definition be "you must implement at least one of `equal` and `not equal`".

A more realistic example would probably be performance related — one function is easier to implement, while the other theoretically allows a more performant implementation (see `Read::{read, read_buf}`).

### Current status

This is currently implemented as an internal unstable rustc attribute `#[rustc_must_implement_one_of]`. It accepts a list of identifiers of trait items and adds a requirement that **at least one** of the items from the list is implemented. A usage example:

```rust
// `read` and `read_buf` are mutually recursive, it would be bad to implement neither
#[rustc_must_implement_one_of(read, read_buf)]
pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        let mut buf = ReadBuf::new(buf);
        self.read_buf(&mut buf)?;
        Ok(buf.filled_len())
    }

    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
        default_read_buf(|b| self.read(b), buf)
    }
}

impl Read for Ty0 {} 
//^ This will fail to compile even though all `Read` methods have default implementations

// Both of these will compile just fine
impl Read for Ty1 {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> { /* ... */ }
}
impl Read for Ty2 {
    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> { /* ... */ }
}
```

It is hopefully not the final form of the attribute, but just a "MVP". Some ideas that we might want to explore in the future:
- A way to specify multiple sets of items (i.e. `a` is mutually recursive with `b`, `c` is mutually recursive with `d`, so MCD is `(a | b) & (c | d)`)
- Add support for non-function items (recursive constants? is this useful?)
- Consider if the trait-level attribute is a good solution
  - Would something like item-level `#[requires(...)]` be easier to use/reason about?
- Iterate on rustdoc output
- Consider how we can support more complicated cases like the `Iterator`, where a lot of methods can be implemented in terms of each other
  - Would it make sense to have multiple default implementations of an item and choose one of them depending on whatever another item is implemented?
- Write an RFC (!)

### Stability

While the attribute itself is very unstable and "MVP", we can still use it in stable and unstable APIs, if we are sure that this is something we wish to support in the future. Thus, we need to be able to control the stability of default implementations (to be able to test if we even need to change MCD of a trait, for example).

For this we have 2 tools. We can either require an unstable method (`#[rustc_must_implement_one_of(something_stable, something_unstable)]`, we can add a default implementation to `something_stable` without worrying — to use the default implementation one would need to opt-in into unstable feature to implement `something_unstable`), **or** use the `#[rustc_default_body_unstable]` attribute.

`#[rustc_default_body_unstable]` attribute, as with any other stability attributes, allows to set a feature gate to using a default body. For example:
```rust
// in std
pub trait Trait {
    #[rustc_default_body_unstable(feature = "feat", isssue = "none")]
    fn item() {}
}
```
```rust
// in a user crate

impl Trait for Type {} // <-- does not provide an `item` implementation, so it uses the default one
//~^ error: not all trait items implemented, missing: `item`
//~| note: default implementation of `item` is unstable
//~| note: use of unstable library feature 'feat'
//~| help: add `#![feature(constant_default_body)]` to the crate attributes to enable

// this is fine
impl Trait for AnotherType {
    fn item() { println!("hehe"); }
}
```

### About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however *not* meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

### Steps
<!--
Include each step required to complete the feature. Typically this is a PR
implementing a feature, followed by a PR that stabilises the feature. However
for larger features an implementation could be broken up into multiple PRs.
-->

- [x] Implement an MVP
- [ ] Write an RFC
- [ ] Implement the RFC
- [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide])
- [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide])

[stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr
[doc-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#documentation-prs

### Unresolved Questions
<!--
Include any open questions that need to be answered before the feature can be
stabilised.
-->

See the "Current status".

### Implementation history

<!--
Include a list of all the PRs that were involved in implementing the feature.
-->

- https://github.com/rust-lang/rust/pull/92164
- https://github.com/rust-lang/rust/pull/93386
- https://github.com/rust-lang/rust/pull/96478
- https://github.com/rust-lang/rust/pull/99235
- https://github.com/rust-lang/rust/pull/105506

### (potential) Uses

- https://github.com/rust-lang/rust/pull/106643