Skip to content

Commit ed438b9

Browse files
committed
Update impl-trait from edition-guide
Add impl-trait text from edition-guide, mostly unchanged except for reformatting to one sentence per line.
1 parent 5aa457b commit ed438b9

File tree

1 file changed

+146
-6
lines changed

1 file changed

+146
-6
lines changed

src/types/impl-trait.md

Lines changed: 146 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@
55
>
66
> _ImplTraitTypeOneBound_ : `impl` [_TraitBound_]
77
8-
## Anonymous type parameters
8+
`impl Trait` is the new way to specify unnamed but concrete types that
9+
implement a specific trait.
10+
There are two places you can put it: argument position, and return position.
11+
12+
```rust,ignore
13+
trait Trait {}
14+
15+
// argument position
16+
fn foo(arg: impl Trait) {
17+
}
918
10-
> Note: This section is a placeholder for more comprehensive reference
11-
> material.
19+
// return position
20+
fn foo() -> impl Trait {
21+
}
22+
```
23+
## Anonymous type parameters
1224

1325
> Note: This is often called "impl Trait in argument position".
1426
@@ -18,11 +30,28 @@ parameter and the function can only use the methods available by the trait
1830
bounds of the anonymous type parameter.
1931

2032
They are written as `impl` followed by a set of trait bounds.
33+
In argument position, this feature is quite simple.
34+
These two forms are almost the same:
2135

22-
## Abstract return types
36+
```rust,ignore
37+
trait Trait {}
38+
39+
fn foo<T: Trait>(arg: T) {
40+
}
2341
24-
> Note: This section is a placeholder for more comprehensive reference
25-
> material.
42+
fn foo(arg: impl Trait) {
43+
}
44+
```
45+
46+
That is, it's a slightly shorter syntax for a generic type parameter.
47+
It means, "`arg` is an argument that takes any type that implements the `Trait` trait."
48+
49+
However, there's also an important technical difference between `T: Trait` and `impl Trait` here.
50+
When you write the former, you can specify the type of `T` at the call site with turbo-fish syntax as with `foo::<usize>(1)`.
51+
In the case of `impl Trait`, if it is used anywhere in the function definition, then you can't use turbo-fish at all.
52+
Therefore, you should be mindful that changing both from and to `impl Trait` can constitute a breaking change for the users of your code.
53+
54+
## Abstract return types
2655

2756
> Note: This is often called "impl Trait in return position".
2857
@@ -33,5 +62,116 @@ type.
3362

3463
They are written as `impl` followed by a set of trait bounds.
3564

65+
Before `impl Trait`, you could do this with trait objects:
66+
67+
```rust
68+
trait Trait {}
69+
70+
impl Trait for i32 {}
71+
72+
fn returns_a_trait_object() -> Box<dyn Trait> {
73+
Box::new(5)
74+
}
75+
```
76+
77+
However, this has some overhead: the `Box<T>` means that there's a heap allocation here, and this will use dynamic dispatch.
78+
See the `dyn Trait` section for an explanation of this syntax.
79+
But we only ever return one possible thing here, the `Box<i32>`.
80+
This means that we're paying for dynamic dispatch, even though we don't use it!
81+
82+
With `impl Trait`, the code above could be written like this:
83+
84+
```rust
85+
trait Trait {}
86+
87+
impl Trait for i32 {}
88+
89+
fn returns_a_trait_object() -> impl Trait {
90+
5
91+
}
92+
```
93+
94+
Here, we have no `Box<T>`, no trait object, and no dynamic dispatch.
95+
But we still can obscure the `i32` return type.
96+
97+
With `i32`, this isn't super useful.
98+
But there's one major place in Rust where this is much more useful: closures.
99+
100+
### `impl Trait` and closures
101+
102+
> If you need to catch up on closures, check out [their chapter in the
103+
> book](https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html).
104+
105+
In Rust, closures have a unique, un-writable type.
106+
They do implement the `Fn` family of traits, however.
107+
This means that previously, the only way to return a closure from a function was to use a trait object:
108+
109+
```rust
110+
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
111+
Box::new(|x| x + 1)
112+
}
113+
```
114+
115+
You couldn't write the type of the closure, only use the `Fn` trait.
116+
That means that the trait object is necessary. However, with `impl Trait`:
117+
118+
```rust
119+
fn returns_closure() -> impl Fn(i32) -> i32 {
120+
|x| x + 1
121+
}
122+
```
123+
124+
We can now return closures by value, just like any other type!
125+
126+
## More details
127+
128+
The above is all you need to know to get going with `impl Trait`, but for some more nitty-gritty details: type parameters and `impl Trait` work slightly differently when they're in argument position versus return position.
129+
Consider this function:
130+
131+
```rust,ignore
132+
fn foo<T: Trait>(x: T) {
133+
```
134+
135+
When you call it, you set the type, `T`.
136+
"you" being the caller here.
137+
This signature says "I accept any type that implements `Trait`."
138+
("any type" == universal in the jargon)
139+
140+
This version:
141+
142+
```rust,ignore
143+
fn foo<T: Trait>() -> T {
144+
```
145+
146+
is similar, but also different.
147+
You, the caller, provide the type you want, `T`, and then the function returns it.
148+
You can see this in Rust today with things like parse or collect:
149+
150+
```rust,ignore
151+
let x: i32 = "5".parse()?;
152+
let x: u64 = "5".parse()?;
153+
```
154+
155+
Here, `.parse` has this signature:
156+
157+
```rust,ignore
158+
pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err> where
159+
F: FromStr,
160+
```
161+
162+
Same general idea, though with a result type and `FromStr` has an associated type... anyway, you can see how `F` is in the return position here.
163+
So you have the ability to choose.
164+
165+
With `impl Trait`, you're saying "hey, some type exists that implements this trait, but I'm not gonna tell you what it is."
166+
So now, the caller can't choose, and the function itself gets to choose.
167+
If we tried to define parse with `Result<impl F,...` as the return type, it wouldn't work.
168+
169+
### Using `impl Trait` in more places
170+
171+
As previously mentioned, as a start, you will only be able to use `impl Trait` as the argument or return type of a free or inherent function.
172+
However, `impl Trait` can't be used inside implementations of traits, nor can it be used as the type of a let binding or inside a type alias.
173+
Some of these restrictions will eventually be lifted.
174+
For more information, see the [tracking issue on `impl Trait`](https://github.com/rust-lang/rust/issues/34511).
175+
36176
[_TraitBound_]: ../trait-bounds.md
37177
[_TypeParamBounds_]: ../trait-bounds.md

0 commit comments

Comments
 (0)