---
title: 2023-08-21 associated type bounds deep dive
date: 2023-08-21
tags: t-types, deep-dive
url: https://hackmd.io/ewHdamprR2WuCMXr0yIUow
discussion: https://rust-lang.zulipchat.com/#narrow/stream/326132-t-types.2Fmeetings/topic/2023-08-21.20associated.20type.20bounds
---
# 2023-08-21 associated type bounds deep dive
RFC: https://github.com/rust-lang/rfcs/blob/master/text/2289-associated-type-bounds.md
Tracking issue: https://github.com/rust-lang/rust/issues/85307
Open issues: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AF-associated_type_bounds
## Summary of feature
There are few different places the feature can be used:
### Where bounds
```rust
trait A { type Item; }
trait B {}
fn ty_bound<X>(_: X) where X: A<Item: B> {}
fn region_bound<X>(_: X) where X: A<Item: 'static> {}
```
and is desugared into
```rust
fn ty_bound<X>(_: X) where X: A, <X as A>::Item: B {}
fn region_bound<X>(_: X) where X: A, <X as A>::Item: 'static {}
```
### Impl trait
```rust
trait A { type Item; }
trait B {}
fn ty_bound<X>() -> impl A<Item: B> {}
fn region_bound<X>() -> impl A<Item: 'static> {}
```
and is desugared into
```rust
fn ty_bound<X>() -> impl A<Item = impl B {}
fn region_bound<X>() -> impl A<Item = ...> // ? not sure
```
## Interactions with HRTBs
Consider the following
```rust
trait I<'a, 'b, 'c> {
type As;
}
trait H<'d, 'e>: for<'f> I<'d, 'f, 'e> {}
fn foo<T>()
where
T: for<'g> H<'g, 'g, As: for<'h> H<'h, 'g> + 'g>,
{
}
```
This would be desugared to
```rust
fn foo<T>()
where
for<'g> T: H<'g, 'g>,
for<'g, 'h> <T as H<'g, 'g>>::As: H<'h, 'g> +'g,
{
}
```
Note that the desugared clause has binders concatenated across levels.
## Desugaring of associated types
RFC specifies as new associated type: https://github.com/rust-lang/rfcs/blob/master/text/2289-associated-type-bounds.md#the-desugaring-for-associated-types
```rust
trait TraitB { type AssocB; }
trait TraitC {}
trait TraitA {
type AssocA: TraitB<AssocB: TraitC>;
}
```
into
```rust
trait TraitA {
type AssocA: TraitB<AssocB = Self::AssocA_0>;
type AssocA_0: TraitC;
}
```
but I think we actually desugar to
```rust
trait TraitA {
type AssocA: TraitB where <Self::AssocA as TraitB>::AssocB: TraitC;
}
```
given the following error (https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=35b4e1751defb1092fbb02981af35666)
```rust
error[E0277]: the trait bound `<<Self as TraitA>::AssocA as TraitB>::AssocB: TraitC` is not satisfied
--> src/lib.rs:7:33
|
7 | type AssocA: TraitB<AssocB: TraitC>;
| ^^^^^^ the trait `TraitC` is not implemented for `<<Self as TraitA>::AssocA as TraitB>::AssocB`
|
help: consider further restricting the associated type
|
6 | trait TraitA where <<Self as TraitA>::AssocA as TraitB>::AssocB: TraitC {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
```
(that suggestion causes it to pass)
## TAIT desugaring
RFC specifies
```rust
trait Bound {}
trait Trait {
type Assoc;
}
impl Trait for () {
type Assoc = ();
}
impl Bound for () {}
fn constrain_foo() -> Foo {}
type Foo = impl Trait<Assoc: Bound>;
```
to desugar to
```rust
type Foo = impl Trait<Assoc = _0>;
type _0 = impl Bound;
```
## Return type notation
Tracking issue: https://github.com/rust-lang/rust/issues/109417
Basically, this is allowed:
```rust
trait Foo {
async fn bar<T>() {}
}
fn test<T>()
where
T: Foo<bar(): Send>,
{
}
```
## Comments and questions
### Desugaring of associated types seems broken, right?
nikomatsakis: The result of [this example from the doc](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=35b4e1751defb1092fbb02981af35666) is an error, but that seems wrong, right? The desugaring from the RFC (to an anonymous associated type) seems "not wrong", it strikes me as quite analogous to TAIT. That said, the desugaring isn't the only way you could do it, though Rust kind of lacks the syntax to express quite what we want, which is a kind of implied bound. TAIT desugaring seems roughly analogous / same thing.
### TAIT desugaring error in document
TC: The document above shows an error for TAIT, but this example works:
```rust
#![feature(type_alias_impl_trait)]
trait Bound {}
trait Trait {
type Assoc;
}
impl Trait for () {
type Assoc = ();
}
impl Bound for () {}
type Foo = impl Trait<Assoc = _0>;
type _0 = impl Bound;
fn constrain_foo() -> Foo {}
```
Correspondingly, so does:
```rust
#![feature(associated_type_bounds)]
#![feature(type_alias_impl_trait)]
trait Bound {}
trait Trait {
type Assoc;
}
impl Trait for () {
type Assoc = ();
}
impl Bound for () {}
fn constrain_foo() -> Foo {}
type Foo = impl Trait<Assoc: Bound>;
```
(The meeting consensus was that this does indeed work and the document was in error.)
### Interaction with a-mir-formality
nikomatsakis: Strikes me as a good candidate to prototype in a-mir-formality as well.
### Desugaring of `'static` bound in RPIT
@_**Jack Huey|232957** [said](https://rust-lang.zulipchat.com/#narrow/stream/326132-t-types.2Fmeetings/topic/2023-08-21/near/386402396):
> Because even the RPIT desugaring is not completely clear to me in cases like this:
```rust
fn region_bound<X>() -> impl A<Item: 'static> {}
```
TC: I'd expect it to desugar as approximately:
```rust
#![feature(type_alias_impl_trait)]
trait Trait {
type Assoc;
}
impl Trait for () {
type Assoc = ();
}
// fn region_bound<X>() -> impl Trait<Assoc: 'static> {}
//
// desugars to:
trait Top {}
impl<T: ?Sized> Top for T {}
type _0 = impl Trait<Assoc = _1>;
type _1 = impl Top + 'static;
fn region_bound<X>() -> _0 {}
```
(CE pointed out that `impl ?Sized + 'static` is valid and may be the appropriate desugaring.)