# Conditional default trait method impls

**URL:** <https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412>\
**Category:** language design\
**Created:** [October 2, 2021, 8:36pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412 "2021-10-02T20:36:26Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Diggsey](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/diggsey/32/6311_2.png) [@Diggsey](https://internals.rust-lang.org/u/Diggsey)\
**Post date:** [October 2, 2021, 8:36pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/1 "2021-10-02T20:36:26Z")

</div>

There's been an issue for a long time with code such as the following:

```rust
fn require_sized<T: Sized>(_: &T) {}

trait Foo {
    fn foo(&self) {
        require_sized(self);
    }
}

struct Bar;
impl Foo for Bar {}

fn main() {
    let x: &dyn Foo = &Bar;
    x.foo();
}

```

Specifically, there's no way to provide the default implementation of `Foo::foo` without also breaking the object safety of `Foo` by adding `Self: Sized` onto the trait or method.

This is an artificial limitation, because although the `dyn Foo` type does implement the `Foo` trait, it does not use the default implementation of `Foo::foo` - a trait object's method implementation always defers to the corresponding method on the underlying type (where `Self: Sized` holds).

The problem with just relaxing this limitation is that there are other unsized types where the default trait implementation would not hold.

I came up with the following idea to solve this:

```rust
trait Foo {
    fn foo(&self) if Self: Sized {
        require_sized(self);
    }
}

```

The idea behind `if Self: Sized` is that when a user implement the `Foo` trait, a default implementation will only be provided if the bound `Self: Sized` holds. If the bound does not hold, then `foo` is just a normal method that the user must implement themselves.

As a result, the default implementation is free to rely on these additional bounds.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [October 2, 2021, 10:32pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/2 "2021-10-02T22:32:14Z")

</div>

[See also.](https://github.com/rust-lang/rust/issues/51402)

I believe specialization gives this to you:

```rust
trait Foo {
    fn foo(&self);
}

default impl<T: Sized> Foo for T {
    fn foo(&self) {
        require_sized(self);
    }
}

```

Though maybe that could be spelled

```rust
trait Foo {
    fn foo(&self) default where Self: Sized {
        require_sized(self);
    }
}

```

---

<div class="post-metadata">

**Author:** ![Diggsey](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/diggsey/32/6311_2.png) [@Diggsey](https://internals.rust-lang.org/u/Diggsey)\
**Post date:** [October 2, 2021, 10:42pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/3 "2021-10-02T22:42:26Z")

</div>

That's slightly different because it actually implements the trait for all those types, which you might not want. Also, if the trait has more than one item then this is not possible.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [October 2, 2021, 10:48pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/4 "2021-10-02T22:48:52Z")

</div>

I'd love a way to have more conditionals on when a default implementation is available. Imagine `size_hint()` defaulting to `(len(), Some(len()))` if you implement `ExactSizeIterator`, for example, or `Iterator::next()` defaulting to `try_for_each(ControlFlow::Break).break_value()` if you've overridden `try_fold`.

(Or even, for compilation time improvements, only implementing `fold` in terms of `try_fold` if you're provided an implementation for the latter.)

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [October 2, 2021, 10:49pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/5 "2021-10-02T22:49:41Z")

</div>

[It doesn't implement the trait for all those types.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c0be5bf40cc9a6c22614ce480446cb9f)

[RFC](https://rust-lang.github.io/rfcs/1210-impl-specialization.html#reuse)

> The specialization design in this RFC also allows for _default impls_ , which can provide specialized defaults without actually providing a full trait implementation
> 
> [...]
> 
> This default impl does _not_ mean that `Add` is implemented for all `Clone` data, but just that when you do impl `Add` and `Self: Clone` , you can leave off `add_assign`

And you can have as many items in there as you want (or don't). [More examples.](https://rust-lang.github.io/rfcs/1210-impl-specialization.html#default-impls)

---

<div class="post-metadata">

**Author:** ![Diggsey](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/diggsey/32/6311_2.png) [@Diggsey](https://internals.rust-lang.org/u/Diggsey)\
**Post date:** [October 2, 2021, 10:56pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/6 "2021-10-02T22:56:28Z")

</div>

My mistake, it seems that does solve the same problem 🙂 - I was getting confused between the use of `default` on an impl item and `default` on the trait implementation itself.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [October 2, 2021, 11:01pm UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/7 "2021-10-02T23:01:20Z")

</div>

I agree the "phrasing" is confusing.

I wonder if it's possible to split this part of the RFC off ahead of time, e.g. if you could only have one blanket `default impl` per item (so there was no actual specialization involved).

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [October 3, 2021, 12:15am UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/8 "2021-10-03T00:15:07Z")

</div>

> [@quinedot](#):
>
> I believe specialization gives this to you

It does, but IMO having this kind of feature connected to specialization is a big mistake. Even with the current implementation on nightly, the fact that such a `default impl` (big misnomer btw.) creates issues with coherence/overlap (I could try to produce a code example if you like) clearly demonstrates that connecting conditionally available default implementations of methods with specialization is a _bad_ idea. (But of course the biggest problem is that - technically - default method implementations aren't _actual_ specialization, they don't come with any soundness issues, if implemented correctly, and the feature is unnecessary held back if it has to wait for specialization to solve all of its soundness issues before it can be stabilized.)

I think that the thing @scottmcm proposes is the right way forward in the long run: considering other forms of conditional default implementations that make sense. We should try to make this a separate thing. In my opinion we'd want to have e.g. things like choosing different default implementations based on which methods were provided manually. We could support different (minimal) sets of methods that need to be implemented for a trait. To name a stupid/simple example, someone implementing `PartialOrd` could choose to implement `partial_cmp` or `lt` or `le` or `gt` or `ge`. The remaining functions could be implemented in terms of the manually provided one, whichever it is. The `ExactSizeIterator` example that @scottmcm also gives suggests things like the ability to implicitly/by default implement `PartialEq`'s methods in term of `partial_cmp`, if the type also implements `PartialOrd`; but considering entire trait hierarchies like that seems even more complex - maybe we shouldn't try to do too much at once?

I also think that a minimal viable product for default method implementations with additional trait bound, similar (at least in terms of capability, perhaps also in terms of syntax) to GHC Haskell's [`DefaultSignatures`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/default_signatures.html) feature, can be a good way to get _something_ useful eventually stabilized **without** needing to get specialization stabilized and also without needing to finish and stabilize a complete general solution/framework of conditional default method implementations.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [October 3, 2021, 1:16am UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/9 "2021-10-03T01:16:47Z")

</div>

> [@steffahn](#):
>
> It does, but IMO having this kind of feature connected to specialization is a big mistake.

It's already part of the RFC, and whatever default impl capabilities arise will have to interact with specialization in one way or another. Or more accurately, coherence will have to contend with both.

Maybe I'm missing your point or some proposal.

> [@steffahn](#):
>
> We could support different (minimal) sets of methods that need to be implemented for a trait.

[(Open) RFC 628.](https://github.com/rust-lang/rfcs/issues/628)

I believe this also came up before, when a `std` trait was modified to deprecate a previously required method and add a new method that did things more correctly. I was actually looking for it earlier today for unrelated reasons, but failed to find it, if anyone happens to know what I'm talking about. Anyway, if my memory serves: either method could be implemented in terms of the other, but if both default bodies were included in such a way, you would get infinite recursion unless you overrode one of them. That was deemed too foot-gunny, and instead only one of the default methods is in terms of the other. Ideally, it would have instead been "you have to implement method `a` or (deprecated) method `b`."

However, this seems a lot _more_ complicated to me than the ability to have a bound on default methods / partial implementations (i.e. the OP). It's a whole new thing that will need to be first approved by RFC. Splitting off `partial impl` will require settling some questions (bike-shed and probably the big one is "can the methods within be final'), but still seems more easily obtainable to me.

> [@steffahn](#):
>
> I also think that a minimal viable product for default method implementations with additional trait bound, similar (at least in terms of capability, perhaps also in terms of syntax) to GHC Haskell's [`DefaultSignatures`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/default_signatures.html) feature, can be a good way to get _something_ useful eventually stabilized **without** needing to get specialization stabilized and also without needing to finish and stabilize a complete general solution/framework of conditional default method implementations.

Can you flesh this out a bit and/or sketch it in Rust syntax? Is it any different from splitting off `partial impl` from the specialization RFC?

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [October 3, 2021, 1:57am UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/10 "2021-10-03T01:57:20Z")

</div>

> [@quinedot](#):
>
> I believe this also came up before, when a `std` trait was modified to deprecate a previously required method and add a new method that did things more correctly. I was actually looking for it earlier today for unrelated reasons, but failed to find it, if anyone happens to know what I'm talking about.

I found it: [RFC 2930: `ReadBuf`](https://rust-lang.github.io/rfcs/2930-read-buf.html) which was updated to not provide a default body for `read` [after this comment and the following conversation.](https://github.com/rust-lang/rfcs/pull/2930#issuecomment-632673777) (Not yet implemented much less stabilized.)

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [October 3, 2021, 7:04am UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/11 "2021-10-03T07:04:07Z")

</div>

A previous sortof-related thread: [Brainstorming: allowing implementing just `try_fold`, and getting `next` free](https://internals.rust-lang.org/t/brainstorming-allowing-implementing-just-try-fold-and-getting-next-free/9417)

> [@quinedot](#):
>
> I wonder if it's possible to split this part of the RFC off ahead of time, e.g. if you could only have one blanket `default impl` per item (so there was no actual specialization involved).

This makes me think of a potential way here of like a "bundle" kind of thing, where you implement that to get an implementation of a different trait (or maybe even a set of traits).

This would be sortof like the thing that people seem to want to do often of "anything that implements this trait gets a free implementation of `Iterator`" that doesn't work because of the blanket impl. But maybe by making that part of the bundle/mixing/whatever it would be ok because the rule would be more specific and the compiler would check it more specifically.

(Very undeveloped idea, as I'm sure you can tell.)

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [January 1, 2022, 7:05am UTC](https://internals.rust-lang.org/t/conditional-default-trait-method-impls/15412/12 "2022-01-01T07:05:02Z")

</div>

Closed on January 1, 2022, 7:05am UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
