Skip to content

Commit efa85fc

Browse files
committed
improve early-late-parameters.md
1 parent fa09a2c commit efa85fc

1 file changed

Lines changed: 12 additions & 12 deletions

File tree

src/early-late-parameters.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The builtin impls for the `FnMut`/`FnOnce` traits as well as the impls for `Copy
3737

3838
A slightly more complicated example would involve introducing generic parameters to the function:
3939
```rust
40-
fn foo<T: Sized>(a: T) -> T {
40+
fn foo<T: Sized>(a: T) -> T {
4141
# a
4242
/* snip */
4343
}
@@ -73,7 +73,7 @@ impl<'a, T: Sized> Fn<(&'a T,)> for FooFnItem<T> {
7373
The lifetime parameter `'a` from the function `foo` is not present on the function item type `FooFnItem` and is instead introduced on the builtin impl solely for use in representing the argument types.
7474

7575
Generic parameters not all being defined on the function item type means that there are two steps where generic arguments are provided when calling a function.
76-
1. Naming the function (e.g. `let a = foo;`) the arguments for `FooFnItem` are provided.
76+
1. Naming the function (e.g. `let a = foo;`) the arguments for `FooFnItem` are provided.
7777
2. Calling the function (e.g. `a(&10);`) any parameters defined on the builtin impl are provided.
7878

7979
This two-step system is where the early vs late naming scheme comes from, early bound parameters are provided in the *earliest* step (naming the function), whereas late bound parameters are provided in the *latest* step (calling the function).
@@ -99,10 +99,10 @@ my_func(&String::new());
9999

100100
## Differences between early and late bound parameters
101101

102-
### Higher ranked function pointers and trait bounds
102+
### Higher ranked function pointers and trait bounds
103103

104104
A generic parameter being late bound allows for more flexible usage of the function item.
105-
For example if we have some function `foo` with an early bound lifetime parameter and some function `bar` with a late bound lifetime parameter `'a` we would have the following builtin `Fn` impls:
105+
For example, if we have some function `foo` with an early bound lifetime parameter and some function `bar` with a late bound lifetime parameter `'a`, we would have the following builtin `Fn` impls:
106106
```rust,ignore
107107
impl<'a> Fn<(&'a String,)> for FooFnItem<'a> { /* ... */ }
108108
impl<'a> Fn<(&'a String,)> for BarFnItem { /* ... */ }
@@ -130,12 +130,12 @@ f(&String::new());
130130
f(&String::new());
131131
```
132132

133-
In this example we call `foo`'s function item type twice, each time with a borrow of a temporary.
134-
These two borrows could not possible have lifetimes that overlap as the temporaries are only alive during the function call, not after.
133+
In this example, we call `foo`'s function item type twice, each time with a borrow of a temporary.
134+
These two borrows could not possibly have lifetimes that overlap as the temporaries are only alive during the function call, not after.
135135
The lifetime parameter on `foo` being early bound requires all callers of `f` to provide a borrow with the same lifetime, as this is not possible the borrow checker errors.
136136

137-
If the lifetime parameter on `foo` was late bound this would be able to compile as each caller could provide a different lifetime argument for its borrow.
138-
See the following example which demonstrates this using the `bar` function defined above:
137+
If the lifetime parameter on `foo` was late bound, this would be able to compile as each caller could provide a different lifetime argument for its borrow.
138+
See the following example, which demonstrates this using the `bar` function defined above:
139139

140140
```rust
141141
# fn foo<'a: 'a>(b: &'a String) -> &'a String { b }
@@ -172,14 +172,14 @@ fn higher_ranked_trait_bound() {
172172
fn higher_ranked_fn_ptr() {
173173
let bar_fn_item = bar;
174174
let fn_ptr: for<'a> fn(&'a String) -> &'a String = bar_fn_item;
175-
175+
176176
let foo_fn_item = foo::<'_>;
177177
// errors
178178
let fn_ptr: for<'a> fn(&'a String) -> &'a String = foo_fn_item;
179179
}
180180
```
181181

182-
In both of these cases the borrow checker errors as it does not consider `foo_fn_item` to be callable with a borrow of any lifetime.
182+
In both of these cases, the borrow checker errors as it does not consider `foo_fn_item` to be callable with a borrow of any lifetime.
183183
This is due to the fact that the lifetime parameter on `foo` is early bound, causing `foo_fn_item` to have a type of `FooFnItem<'_>` which (as demonstrated by the desugared `Fn` impl) is only callable with a borrow of the same lifetime `'_`.
184184

185185
### Turbofishing in the presence of late bound parameters
@@ -208,7 +208,7 @@ let f /* : FooFnItem<'static> */ = foo::<'static>;
208208

209209
What the current implementation of the compiler aims to do is error when specifying lifetime arguments to a function that has both early *and* late bound lifetime parameters.
210210
In practice, due to excessive breakage, some cases are actually only future compatibility warnings ([#42868](https://github.com/rust-lang/rust/issues/42868)):
211-
- When the amount of lifetime arguments is the same as the number of early bound lifetime parameters a FCW is emitted instead of an error
211+
- When the amount of lifetime arguments is the same as the number of early bound lifetime parameters, a FCW is emitted instead of an error
212212
- An error is always downgraded to a FCW when using method call syntax
213213

214214
To demonstrate this we can write out the different kinds of functions and give them both a late and early bound lifetime:
@@ -330,7 +330,7 @@ fn bar<T>() {
330330
```
331331

332332
As the type parameter `T` is early bound, the desugaring of the function item type for `foo` would look something like `struct FooFnItem<T>`.
333-
Then in order for `FooFnItem<T>: 'static` to hold we must also require `T: 'static` to hold as otherwise we would wind up with soundness bugs.
333+
Then, in order for `FooFnItem<T>: 'static` to hold, we must also require `T: 'static` to hold as otherwise we would wind up with soundness bugs.
334334

335335
Unfortunately, due to bugs in the compiler, we do not take into account early bound *lifetimes*, which is the cause of the open soundness bug [#84366](https://github.com/rust-lang/rust/issues/84366).
336336
This means that it's impossible to demonstrate a "difference" between early/late bound parameters for liveness/type outlives bounds as the only kind of generic parameters that are able to be late bound are lifetimes which are handled incorrectly.

0 commit comments

Comments
 (0)