--- 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.)