You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
74
74
75
75
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.
77
77
2. Calling the function (e.g. `a(&10);`) any parameters defined on the builtin impl are provided.
78
78
79
79
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());
99
99
100
100
## Differences between early and late bound parameters
101
101
102
-
### Higher ranked function pointers and trait bounds
102
+
### Higher ranked function pointers and trait bounds
103
103
104
104
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:
106
106
```rust,ignore
107
107
impl<'a> Fn<(&'a String,)> for FooFnItem<'a> { /* ... */ }
108
108
impl<'a> Fn<(&'a String,)> for BarFnItem { /* ... */ }
@@ -130,12 +130,12 @@ f(&String::new());
130
130
f(&String::new());
131
131
```
132
132
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.
135
135
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.
136
136
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:
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.
183
183
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 `'_`.
184
184
185
185
### Turbofishing in the presence of late bound parameters
@@ -208,7 +208,7 @@ let f /* : FooFnItem<'static> */ = foo::<'static>;
208
208
209
209
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.
210
210
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
212
212
- An error is always downgraded to a FCW when using method call syntax
213
213
214
214
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>() {
330
330
```
331
331
332
332
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.
334
334
335
335
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).
336
336
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