# Pre-RFC: Associated type inference

**URL:** <https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886>\
**Category:** language design\
**Created:** [December 31, 2021, 9:03pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886 "2021-12-31T21:03:25Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![ibraheemdev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ibraheemdev/32/13406_2.png) [@ibraheemdev](https://internals.rust-lang.org/u/ibraheemdev)\
**Post date:** [December 31, 2021, 9:03pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/1 "2021-12-31T21:03:25Z")

</div>

# Summary

Remove the need for explicit associated type definitions when implementing a trait where it can be inferred from a local trait method.

# Motivation

When implementing a trait with generics or associated types, you often have to repeat types multiple times. For example:

```rust
impl Iterator for Foo {
    type Item = usize;
    fn next(&mut self) -> Option<usize> { ... }
}

```

Or a more complex one:

```rust
impl<S, R> Service<R> for Timeout<S>
where
    S: Service<R>
{
    type Response = S::Response;
    type Error = E;
    type Future = ResponseFuture<S::Future>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { ... }
    fn call(&mut self, request: R) -> Self::Future { ... }
}

```

Having to write out associated type definitions makes implementing a trait feel much heavier than writing an inherent method. This proposal introduces an inference source for these types, local trait methods.

```rust
impl Iterator for Foo {
    fn next(&mut self) -> Option<usize> { ... } // `Item = usize` is inferred
}

```

# Guide-level explanation

An associated type can only be inferred if a concrete type is used in it's place in a trait method. This means that `Iterator::Item` can be inferred from the return type of `next`:

```rust
impl Iterator for Foo {
    fn next(&mut self) -> Option<usize> { ... } // `Item = usize`
}

```

However, `IntoIterator::Item` will _not_ be:

```rust
impl IntoIterator for Foo {
    type Item = usize; // Still required
    fn into_iter(self) -> FooIntoIter { ... } // `Iter = FooIntoIter`
}

```

Although it could be inferred from `<Self::Iter as Iterator>::Item`, this would significantly affect reasoning about the code as you would have to jump to a second trait implementation to find the type.

# Drawbacks

Associated type inference removes some context about the trait from implementations. You will not be able to look at a `FromStr` implementation and see directly what the `Error` type is. Instead, you will have to look at the return type of the `from_str` method. A lot of this could be mitigated by having rustdoc retain full associated type definitions.

# Alternatives

## Type Elision

An alternative approach to removing the need for the type definition completely is using the `_` placeholder:

```rust
impl Iterator for Foo {
    type Item = _;
    fn next(&mut self) -> Option<usize> { ... }
}

```

However, the benefit provided this is much less. You still need the `type Item = _` line, yet it doesn't really provide any meaningful information.

Another approach might be to elide the function's parameter/return type:

```rust
impl Iterator for Foo {
    type Item = usize;
    fn next(&mut self) -> Option<_> { ... }
}

```

But this again doesn't help very much, you can already use the `Self::Item` alias instead of writing the type again, and you still need the extra line, `type Item = T`.

## Inferring Generics

Generic types cannot be inferred in this proposal:

```rust
impl From<_> for Foo { // not allowed
   fn from(x: usize) -> Foo { ... }
}

```

The main reason for this is that inferring generics provides little benefit, saving a few characters versus saving an entire line with associated types. Generics also being a direct part of the trait means that scanning code for say, a `From<X>` implementation provides immediate context about a type. `From<_>` would mean an extra step to look at the parameter of the `from` method. With associated types this extra step is already there. Now instead of searching for `impl Iterator` and looking at `type Item`, you search for `impl Iterator` and look at `fn next`.

Generic inference is left as a potential future extension in an effort to keep this RFC minimal.

# Prior art

I believe Haskell performs similar type inference?

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [December 31, 2021, 10:19pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/2 "2021-12-31T22:19:36Z")

</div>

Alternatives should mention using the associated type directly in the implementation to avoid repetition:

```rust
impl Iterator for Foo {
    type Item = usize;
    fn next(&mut self) -> Option<Self::Item> { ... }
}

```

This is my preferred way to write it currently, especially because it's the form you get when you copy-paste the trait definition from the docs and then fill out the bodies.

---

<div class="post-metadata">

**Author:** ![ibraheemdev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ibraheemdev/32/13406_2.png) [@ibraheemdev](https://internals.rust-lang.org/u/ibraheemdev)\
**Post date:** [January 1, 2022, 1:28am UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/3 "2022-01-01T01:28:48Z")

</div>

Thanks, I'll add that. The point of this proposal is to eliminate the extra type definition completely, which gets implementing a trait much closer to writing an inherent method.

---

<div class="post-metadata">

**Author:** ![elidupree](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/elidupree/32/4304_2.png) [@elidupree](https://internals.rust-lang.org/u/elidupree)\
**Post date:** [January 1, 2022, 1:44am UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/4 "2022-01-01T01:44:50Z")

</div>

Oh, this is neat. Writing the extra associated-type line is definitely a minor annoyance for me, and this seems like a logical way to avoid it.

That said, at first glance, I don't feel like this is worth changing the language for, especially since there may be a reduction in code readability. The status quo isn't a serious problem, so I'm inclined to think that the hidden costs that come with adding any language feature outweigh the benefit.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [January 1, 2022, 10:56am UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/5 "2022-01-01T10:56:31Z")

</div>

I wonder if this could allow for a reduced version of `type_alias_impl_trait` but only in traits.

---

<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:** [January 1, 2022, 6:47pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/6 "2022-01-01T18:47:20Z")

</div>

> [@ibraheemdev](#):
>
> Although it could be inferred from `<Self::Iter as Iterator>::Item` , this would significantly affect reasoning about the code as you would have to jump to a second trait implementation to find the type.

I'm not convinced by the difference here. I think if it's important to be blatantly obvious in the source code -- as opposed to in the rustdoc, where it will be shown either way -- then we shouldn't do the feature at all. But if that's not particularly important, as implied by having the feature at all, then I don't see why it's needed for `Item` to be there.

I could probably be persuaded that there's a meaningful distinction, though.

> [@ibraheemdev](#):
>
> Alternatives

I think the big missing alternative here is structured suggestions. For example, `fn foo(x: i32) -> _ { x }` doesn't work, because we want the return type specified, but there's a wonderful structured suggestion to replace the `_` with exactly the right type.

So one stepping stone would be to make it so that it's not just the generic

```rust
help: implement the missing item: `type Output = Type;`

```

but runs the inference and gives the actual `type Output = i32;` or whatever that will work with the rest of the `impl`. (Just like is done for `-> _`.)

And that's "just" a diagnostic change, so doesn't require an RFC or other heavy process like that.

---

<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:** [January 2, 2022, 1:58pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/7 "2022-01-02T13:58:16Z")

</div>

> [@ibraheemdev](#):
>
> # Prior art
> 
> I believe Haskell performs similar type inference?

No. Instead, Haskell doesn't have type signatures on the _methods_ of a trait implementation. Like, at all. You even need to activate an additional [language extension](https://downloads.haskell.org/~ghc/9.2.1/docs/html/users_guide/exts/instances.html#extension-InstanceSigs) to allow them at all, even optionally. Signatures of trait methods are basically completely redundant anyways, so it makes sense. However, [associated types](https://downloads.haskell.org/~ghc/9.2.1/docs/html/users_guide/exts/type_families.html#associated-data-and-type-families) are always required to be specified.1 This is most similar to your alternative

> [@ibraheemdev](#):
>
> Another approach might be to elide the function's parameter/return type:
> 
> ```rust
> impl Iterator for Foo {
> type Item = usize;
> fn next(&mut self) -> Option<_> { ... }
> }
> 
> ```

but you wouldn't need to specify the `Option` either, or the `&mut` for `self`.

 

1Actually, you _can_ leave them out if you want, but the effect is drastically different. The type isn't inferred but just left to be a kind-of abstract unknown type; which is something Haskell can do because it doesn't have monomorphization and all types are boxed, so they all have the same representation at runtime and you don't actually _need_ to know the concrete type for code generation.

---

<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:** [January 2, 2022, 5:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/8 "2022-01-02T17:45:43Z")

</div>

> [@ibraheemdev](#):
>
> Another approach might be to elide the function's parameter/return type:
> 
> ```rust
> impl Iterator for Foo {
> type Item = usize;
> fn next(&mut self) -> Option<_> { ... }
> }
> 
> ```

I think this one is a very different feature. Because it goes down the road of allowing this:

```rust
impl Add<SomeReallyComplicatedType> for Foo {
    type Output = Foo;
    fn add(self, other: _) -> Foo { ... }
}

```

where because it's the impl of a trait, the type there is fully specified anyway, so there's really no _need_ to specify it -- having the compiler just fill it in with the only allowed value is substantially simpler than the inference needed to populate an associated type or TAIT.

(Said without taking a position either way on whether allowing such a thing would be _good_.)

* * *

This also makes me think of another potential idea in the same area as this.

Given a future in which we have trait aliases stabilized, imagine that you have `trait ByteIter = Iterator<Item = u8>;`. Then would could consider allowing

```rust
impl ByteIter for Foo {
    fn next(&mut self) -> Option<Self::Item> { ... }
}

```

without needing the associated type because it's "defined" by the equality constraint in the trait alias.

(Though right now you can't `impl` a trait alias at all, so there's a bunch more design work hiding in there.)

---

<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:** [April 2, 2022, 5:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-associated-type-inference/15886/9 "2022-04-02T17:45:53Z")

</div>

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