Skip to content

Commit d9264ab

Browse files
committed
impl-trait: less update-y; more reference-y
Reword a lot of things to sound less like an update and more like a reference. Add local links in a few places.
1 parent 999a143 commit d9264ab

File tree

1 file changed

+49
-51
lines changed

1 file changed

+49
-51
lines changed

src/types/impl-trait.md

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,63 @@
55
>
66
> _ImplTraitTypeOneBound_ : `impl` [_TraitBound_]
77
8-
`impl Trait` is the new way to specify unnamed but concrete types that
8+
> **Edition differences**: `impl Trait` is new in the 2018 edition.
9+
10+
`impl Trait` provides ways to specify unnamed but concrete types that
911
implement a specific trait.
10-
There are two places you can put it: argument position, and return position.
12+
It can appear in two sorts of places: argument position (where it can act as an anonymous type parameter to functions), and return position (where it can act as an abstract return type).
1113

12-
```rust,ignore
14+
```rust
1315
trait Trait {}
16+
# impl Trait for () {}
1417

15-
// argument position
18+
// argument position: anonymous type parameter
1619
fn foo(arg: impl Trait) {
1720
}
1821

19-
// return position
20-
fn foo() -> impl Trait {
22+
// return position: abstract return type
23+
fn bar() -> impl Trait {
2124
}
2225
```
2326
## Anonymous type parameters
2427

2528
> Note: This is often called "impl Trait in argument position".
2629
27-
Functions can declare an argument to be an anonymous type parameter where the
28-
caller must provide a type that has the bounds declared by the anonymous type
29-
parameter and the function can only use the methods available by the trait
30-
bounds of the anonymous type parameter.
30+
Functions can use `impl` followed by a set of trait bounds to declare an argument as having an anonymous type.
31+
The caller must provide a type that satisfies the bounds declared by the anonymous type parameter, and the function can only use the methods available through the trait bounds of the anonymous type parameter.
3132

32-
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:
33+
For example, these two forms are almost equivalent:
3534

3635
```rust,ignore
3736
trait Trait {}
3837
38+
// generic type parameter
3939
fn foo<T: Trait>(arg: T) {
4040
}
4141
42+
// impl Trait in argument position
4243
fn foo(arg: impl Trait) {
4344
}
4445
```
4546

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."
47+
That is, `impl Trait` in argument position is syntactic sugar for a generic type parameter like `<T: Trait>`, except that the type is anonymous and doesn't appear in the [_GenericParams_] list.
4848

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.
49+
> **Note:**
50+
> For function arguments, generic type parameters and `impl Trait` are not exactly equivalent.
51+
> With a generic parameter such as `<T: Trait>`, the caller has the option to explicitly specify the generic argument for `T` at the call site using [_GenericArgs_], for example, `foo::<usize>(1)`.
52+
> If `impl Trait` is the type of a function argument, then the caller can't ever specify the type of that argument by using a generic argument.
53+
>
54+
> Therefore, changing the function signature from either one to the other can constitute a breaking change for the callers of a function.
5355
5456
## Abstract return types
5557

5658
> Note: This is often called "impl Trait in return position".
5759
58-
Functions, except for associated trait functions, can return an abstract
59-
return type. These types stand in for another concrete type where the
60-
use-site may only use the trait methods declared by the trait bounds of the
61-
type.
62-
63-
They are written as `impl` followed by a set of trait bounds.
60+
Functions can use `impl Trait` to return an abstract return type.
61+
These types stand in for another concrete type where the caller may only use the methods declared by the specified `Trait`.
62+
Each possible return value from the function must resolve to the same concrete type.
6463

65-
Before `impl Trait`, you could do this with trait objects:
64+
Prior to `impl Trait`, a function could express abstract return types by using [trait objects]:
6665

6766
```rust
6867
trait Trait {}
@@ -74,10 +73,9 @@ fn returns_a_trait_object() -> Box<dyn Trait> {
7473
}
7574
```
7675

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!
76+
This has some drawbacks: constructing `Box<T>` involves a heap allocation, and the `dyn Trait` will use dynamic dispatch on its methods.
77+
However, this function only returns one possible type here: the `Box<i32>`.
78+
This means incurring the costs of dynamic dispatch, even though the return type cannot vary.
8179

8280
With `impl Trait`, the code above could be written like this:
8381

@@ -91,19 +89,16 @@ fn returns_a_trait_object() -> impl Trait {
9189
}
9290
```
9391

94-
Here, we have no `Box<T>`, no trait object, and no dynamic dispatch.
95-
But we still can obscure the `i32` return type.
92+
There is no `Box<T>`, no trait object, and no dynamic dispatch.
93+
However, the function can still can obscure the `i32` return type.
9694

97-
With `i32`, this isn't super useful.
98-
But there's one major place in Rust where this is much more useful: closures.
95+
With `i32`, this might not seem very useful.
96+
There is one major place in Rust where this is much more useful: closures.
9997

10098
### `impl Trait` and closures
10199

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.
100+
In Rust, [closures] have a unique, un-writable type.
101+
However, they do implement the `Fn` family of traits.
107102
This means that previously, the only way to return a closure from a function was to use a trait object:
108103

109104
```rust
@@ -112,16 +107,17 @@ fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
112107
}
113108
```
114109

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`:
110+
It wasn't possible to fully specify the type of the closure, only use the `Fn` trait.
111+
That means that the trait object is necessary.
112+
However, with `impl Trait`:
117113

118114
```rust
119115
fn returns_closure() -> impl Fn(i32) -> i32 {
120116
|x| x + 1
121117
}
122118
```
123119

124-
We can now return closures by value, just like any other type!
120+
It is now possible to return closures by value, just like any other type.
125121

126122
## More details
127123

@@ -132,10 +128,8 @@ Consider this function:
132128
fn foo<T: Trait>(x: T) {
133129
```
134130

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)
131+
The caller of this function determines the type, `T`.
132+
This function signature means that the function accepts any type that implements `Trait`."
139133

140134
This version:
141135

@@ -144,15 +138,15 @@ fn foo<T: Trait>() -> T {
144138
```
145139

146140
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:
141+
The caller determines the return type, `T`, and the function returns it.
142+
Examples of this include the `.parse()` or `.collect()` methods:
149143

150144
```rust,ignore
151145
let x: i32 = "5".parse()?;
152146
let x: u64 = "5".parse()?;
153147
```
154148

155-
Here, `.parse` has this signature:
149+
Here, `.parse()` has this signature:
156150

157151
```rust,ignore
158152
pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err> where
@@ -162,8 +156,8 @@ pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err> where
162156
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.
163157
So you have the ability to choose.
164158

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.
159+
With `impl Trait`, the function asserts that the return type will implement this trait, but the caller can't know exactly which type.
160+
So with `impl Trait`, unlike with a generic type parameter for the return type, the caller can't choose the return type, and the function itself gets to choose.
167161
If we tried to define parse with `Result<impl F,...` as the return type, it wouldn't work.
168162

169163
### Using `impl Trait` in more places
@@ -173,5 +167,9 @@ However, `impl Trait` can't be used inside implementations of traits, nor can it
173167
Some of these restrictions will eventually be lifted.
174168
For more information, see the [tracking issue on `impl Trait`](https://github.com/rust-lang/rust/issues/34511).
175169

170+
[closures]: closure.md
171+
[_GenericArgs_]: ../paths.md#paths-in-expressions
172+
[_GenericParams_]: ../items/generics.md
176173
[_TraitBound_]: ../trait-bounds.md
174+
[trait objects]: trait-object.md
177175
[_TypeParamBounds_]: ../trait-bounds.md

0 commit comments

Comments
 (0)