- 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
- An error is always downgraded to a FCW when using method call syntax
To demonstrate this we can write out the different kinds of functions and give them both a late and early bound lifetime:
```rust,ignore
fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
struct Foo;
trait Trait: Sized {
fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ());
fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ());
}
impl Trait for Foo {
fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {}
fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
}
impl Foo {
fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {}
fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
}
```
Then, for the first case, we can call each function with a single lifetime argument (corresponding to the one early bound lifetime parameter) and note that it only results in a FCW rather than a hard error.
```rust
#![deny(late_bound_lifetime_arguments)]
# fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
#
# struct Foo;
#
# trait Trait: Sized {
# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ());
# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ());
# }
#
# impl Trait for Foo {
# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {}
# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
# }
#
# impl Foo {
# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {}
# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
# }
#
// Specifying as many arguments as there are early
// bound parameters is always a future compat warning
Foo.trait_method::<'static>(&(), &());
Foo::trait_method::<'static>(Foo, &(), &());
Foo::trait_function::<'static>(&(), &());
Foo.inherent_method::<'static>(&(), &());
Foo::inherent_function::<'static>(&(), &());
free_function::<'static>(&(), &());
```
For the second case we call each function with more lifetime arguments than there are lifetime parameters (be it early or late bound) and note that method calls result in a FCW as opposed to the free/associated functions which result in a hard error:
```rust
# fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
#
# struct Foo;
#
# trait Trait: Sized {
# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ());
# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ());
# }
#
# impl Trait for Foo {
# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {}
# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
# }
#
# impl Foo {
# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {}
# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {}
# }
#
// Specifying more arguments than there are early
// bound parameters is a future compat warning when
// using method call syntax.
Foo.trait_method::<'static, 'static, 'static>(&(), &());
Foo.inherent_method::<'static, 'static, 'static>(&(), &());
// However, it is a hard error when not using method call syntax.
Foo::trait_method::<'static, 'static, 'static>(Foo, &(), &());
Foo::trait_function::<'static, 'static, 'static>(&(), &());
Foo::inherent_function::<'static, 'static, 'static>(&(), &());
free_function::<'static, 'static, 'static>(&(), &());
```
Even when specifying enough lifetime arguments for both the late and early bound lifetime parameter, these arguments are not actually used to annotate the lifetime provided to late bound parameters. We can demonstrate this by turbofishing `'static` to a function while providing a non-static borrow:
```rust
struct Foo;
impl Foo {
fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b String ) {}
}
Foo.inherent_method::<'static, 'static>(&(), &String::new());
```
This compiles even though the `&String::new()` function argument does not have a `'static` lifetime, this is because "extra" lifetime arguments are discarded rather than taken into account for late bound parameters when actually calling the function.