# impl Trait implementation plan ## Goals * Stabilize what we can * Have a plan to see things through ## Slice 0 Goal: * Enable people to have a traits that return iterators/futures, at least in some cases where GATs aren't needed Proposal: * permit impl Trait in type aliases and associated types * even if the `impl Trait` is not used directly but as a field of an aggregate, e.g. `(&str, impl Trait)` or `Foo<impl Trait>` * forbid type alises containing impl Trait from appearing outside of "return position" * remove the hack that prevents cycles errors when an associated type would be normalized to an opaque type ```rust // OK, because Foo is used in Return Position type Foo = impl Trait; fn foo() -> Foo { } // OK, because `Self::Iter` (in the trait) // is only used in return position? impl IntoIterator for MyType { type IntoIter = impl Iterator<Item = ...>; type Item = u32; fn into_iter(self) -> Self::IntoIter { let x: Self::Iter = ...; /// ^^^^^^^^^^ would get a cycle error from normalization let y: <MyType as IntoIterator>::IntoIter; // what about } } ``` Things to test: * in argument position (should be disallowed) ## Slice 1 * Mingling the opaque + "under inference" type within a fn body * Technically possible today due to recursion, if you are careful, but harder to observe ```rust // Interesting example 1: If we replaced `Foo` with an // inference variable here, as proposed by RFC, then // the method dispatch would error. This is "odd" since // the method dispatch would work ok if `foo` were not in // the defining scope, but maybe ultimately acceptable? type Foo = impl Method; fn foo(x: Foo) { x.method(); // will give errors } // Replacing with inference variables and recursion shows // this "normalizing" has to be done through type-checking: type Foo = impl Method; type Bar = impl Method; fn foo(count: usize) -> Foo { let x: usize = bar(); // return type of `foo` has to be changed 22_usize } fn bar() -> Bar { // is this legal? probably not, as it only partially constrains Foo and Bar. foo() } ``` ## What traits should an opaque type implement? ```rust fn foo() { let a: impl Display = 22; // Per RFC, I don't know that `typeof(x): Debug` } type Foo = impl Display; fn foo() { let b: Foo = 22_usize; // I do know that `typeof(x): Debug`? Not very clear. let c: usize = x; // But we can accept this? } ``` ## What currently happens * replace opaque type with inference variable in return position * in other positions, it remains opaque * we don't error if it's a type-alias impl trait ```rust type Foo = impl Trait; fn foo1() -> Foo { // hence Foo = opaque type is inferred here foo2() // has the opaque type } fn foo2() -> Foo { 222 } ``` compiles today: [playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=8a9a4c8c765a7917af0c3211208392e2) ```rust #![feature(type_alias_impl_trait)] struct MyType { } impl IntoIterator for MyType { type IntoIter = impl Iterator<Item = u32>; type Item = u32; fn into_iter(self) -> Self::IntoIter { if false { // normalizes because of some hack that prevents the // cycle hack. If you don't use `panic!` but rather `vec![22].into_iter()`, you get a type error // here because `Self::IntoIter` is normalized // to the opaque type let x: Self::IntoIter = panic!(); } vec![22].into_iter() } } ``` ## Expected semantics? ```rust fn foo(x: impl Debug) {} ``` `x`'s type is opaque. ```rust #![feature(type_alias_impl_trait)] type Foo = (&'static str, impl std::fmt::Debug); fn foo() -> Foo { ("foo", 42) } ``` works ```rust #![feature(type_alias_impl_trait)] type Foo = impl std::fmt::Debug; fn foo() -> Foo { "foo" } ```