{{#include ../badges.md}}
Impl trait is accepted in the following locations:
Position | Who determines the hidden type | Role | Status |
---|---|---|---|
Argument position | Each caller | Input | ![stable][] |
Type alias | Code within the enclosing module | Output | ![nightly][] |
Return position, free fns | The function body | Output | ![stable][] |
Return position, inherent impls | The function body | Output | ![stable][] |
Return position, trait impls | The function body | Output | ![planning rfc][] |
Return position, traits | The impl | Input | ![planning rfc][] |
Let binding | The enclosing function or code block | Output | ![accepted rfc][] |
Const binding | The const initializer | Output | ![accepted rfc][] |
Static binding | The static initializer | Output | ![accepted rfc][] |
Impl trait is not accepted in other locations; Appendix C covers some of the locations where impl Trait
is not presently accepted and why.
Impl traits in general play one of two roles.
An input impl trait corresponds loosely to a generic parameter. The code that references the impl Trait may be instantiated multiple times with different values. For example, a function using impl Trait in argument position can be called with many different types for the impl Trait.
An output impl trait plays a role similar to a type that is given by the user. In this case, the impl trait represents a single type (although that type may be relative to generic types are in scope) which is inferred from the code around the definition. For example, a free function with an impl Trait in return position will have the true return type inferred from the function body. The type represented by an output impl trait is called the hidden type. The code that is used to infer the value for an output impl trait is called its defining scope.
In general, the role of impl Trait
and '_
both follow the same rules in terms of being "input vs output". When in an argument listing, that is an "input" role and they correspond to a fresh parameter in the innermost binder. Otherwise, they are in "output" role and the corresponding to something which is inferred or selected from context (in the case of '_
in return position, it is selected based on the rules of lifetime elision; '_
within a function body corresponds to inference).
type Foo = impl Trait;
Creates a opaque type whose value will be inferred by the contents of the enclosing module (and its submodules).
fn foo(x: impl Trait)
becomes an "anonymous" generic parameter, analogous to
fn foo<T: Trait>(x: T)
However, when impl Trait
is used on a function, the resulting type parameter cannot be specified using "turbofish" form; its value must be inferred. (status: this detail not yet decided).
Places this can be used:
- Top-level functions and inherent methods
- Trait methods
- Implication: trait is not dyn safe
fn foo() -> impl Trait)
becomes an "anonymous" generic parameter, analogous to
fn foo<T: Trait>(x: T)
type Foo = impl Trait; // defining scope: just the fn
fn foo() -> Foo
Places this can be used:
- Top-level functions and inherent methods
- Trait methods (pending; no RFC)
fn foo() {
let x: impl Debug = ...;
}
becomes a type alias impl trait, analogous to
type Foo = impl Debug; // scope: fn body
fn foo() {
let x: Foo = ...;
}