Type alias impl Trait (TAIT) refers to impl Trait appearing in a type alias. impl Trait
in this position can appear as the entire value of the type alias:
type Alias1 = impl Trait;
Most of this report is concerned only with the 'simple' TAIT described in the previous form, and thus written as if each TAIT has a name. However, we do support some extended forms, which are effectively desugarings. These extended forms are described here.
impl Trait
does not have to appear at the "top level" of a type alias. It may appear embedded within other types:
type Alias2 = (impl Trait, impl Trait);
One can think of the latter case (Alias2
) as a kind of shorthand that expands to multiple named aliases:
type Temp0 = impl Trait;
type Temp1 = impl Trait;
type Alias2 = (Temp0, Temp1);
FIXME: should maybe be a bit more specific here; for example, this doesn't work (should it?):
type Z = fn() -> impl Sized;
fn bar() -> Z {
|| 0u32
}
Impl Trait can also appear in an impl:
impl Service for MyType {
type Future = impl Future<Output = Response>;
fn process_request(&self) -> Self::Future {
async move { ... }
}
}
This can effectively be desugared into a TAIT whose scope encompasses just the impl. Roughly like the following:
FIXME: double check this desugaring is correct
mod _hidden_module {
use super::*;
type Future = impl Future<Output = Response>;
impl Service for MyType {
fn process_request(&self) -> Future {
async move { ... }
}
}
}
A TAIT conceptually creates an "opaque type" whose underlying concrete type is inferred by the compiler. The inference is done based on the other items within the same module as the impl Trait (transitively).
Example:
mod m {
pub type MyFuture = impl Future<Output = u32>;
pub fn foo() -> MyFuture {
async move { 22 }
}
}
References to a type alias impl trait from outside the module treat it like an "opaque alias" (i.e., some type that implements future). Note that it has identity, even if we don't know precisely what team it is:
mod m { .. /* as above */ .. }
pub fn bar() -> m::MyFuture {
foo() // OK
}
Type alias impl Trait requires uses within the module where it is defined. You cannot have a type alias impl trait that is defined by uses outside of the module:
mod m {
pub type MyFuture = impl Future; // Error, no uses from outside
}
pub fn bar() -> m::MyFuture {
foo() // OK
}
The "minimal type alias impl Trait" (MTAIT) up for stabilization today limits the set of places where a TAIT T
is used. To be accepted, a TAIT T
defined in the module m
must be used only in the following places:
m
(in which case, T
is used as an opaque alias)m
:
XXX
The RFC permits TAIT to be used in many locations, most of which are still considered unstable. This is partly because the compiler's inference algorithm is not yet able to handle the full complexity described by the RFC. Here are some examples that remain unstable:
type MyFuture = impl Future;
pub fn foo(x: MyFuture) {
// Illegal: cannot use MyFuture in an argument
}
pub fn foo(x: MyFuture) -> MyFuture {
// Illegal: cannot use MyFuture in an argument
x
}
struct Foo {
x: MyFuture // Illegal: cannot use MyFuture in a field type
}
FIXME(Jack): maybe add a comment about submodule workaround for using type in struct field
This can be used to define traits that return futures, although to truly handle the use case in full one also needs generic associated types. But MTAIT can be used for the tower Service
trait, for example.
impl Trait
in an impl as value of an associated type
impl Service { type Future = impl Future; fn foo() -> Self::Future { ... } }
impl Trait
in a tuple
type Foo = (impl Trait, u32)
impl Trait
in a struct argument
type Foo = Vec<impl Trait>
impl Trait
in an impl as the value for an associated type in a dyn
type Foo = Box<dyn Iterator<Item = impl Debug>>
impl Trait
in an impl as the value for an associated type in an impl trait
type Foo = impl Iterator<Item = impl Debug>
type Bar = impl Debug; type Foo = impl Iterator<Item = Bar>
type Foo = fn(impl Trait)
type Foo = fn(impl Debug)
let
type Bar = impl Debug; type Foo = impl PartialEq<Bar>;
type Bar = impl Debug; fn foo() -> impl PartialEq<Bar> { }
type Foo = impl Trait; impl Foo { type Item = Foo }
type Foo = impl Trait; fn foo() -> Foo { }
-> impl Future<Output = N>
-> impl Trait<N>
fn(N)
FIXME