They're not paired by category exactly. Using the terminology of my last analogy, type and const parameters are in the same list. If you have...
fn foo<T, const U: usize>() {}
...you can...
// Elide everything
foo([42]);
// (Same thing)
foo::<>([42]);
// Specify everything
foo::<i32, 0>([]);
...but you cannot:
// Elide just const parameters
foo::<i32>([]);
// Elide just type parameters
foo::<0>([String::new()]);
error[E0107]: function takes 2 generic arguments but 1 generic argument was supplied
--> src/main.rs:11:5
|
11 | foo::<i32>([]);
| ^^^ --- supplied 1 generic argument
| |
| expected 2 generic arguments
|
note: function defined here, with 2 generic parameters: `T`, `U`
--> src/main.rs:1:4
|
1 | fn foo<T, const U: usize>(_: [T; U]) {}
| ^^^ - --------------
help: add missing generic argument
|
11 | foo::<i32, U>([]);
| +++
error[E0107]: function takes 2 generic arguments but 1 generic argument was supplied
--> src/main.rs:13:5
|
13 | foo::<0>([String::new()]);
| ^^^ - supplied 1 generic argument
| |
| expected 2 generic arguments
|
note: function defined here, with 2 generic parameters: `T`, `U`
--> src/main.rs:1:4
|
1 | fn foo<T, const U: usize>(_: [T; U]) {}
| ^^^ - --------------
help: add missing generic argument
|
13 | foo::<0, U>([String::new()]);
| +++
And similarly this is an error; you must annotate both lifetimes if you annotate one of them.
fn foo<'a: 'b, 'b>(_: &'b &'a str) {}
fn main() {
foo::<'static>(&"");
}
For the lifetime parameters, you can
- Elide all lifetimes
- Annotate all lifetimes, if they are all early bound
- (Annotate early bound lifetimes which are mixed late bound lifetimes, but get a future compatibility warning like was discussed above; I'm not going to dig into every nuance)
For the non-lifetime parameters, you can
- Elide all parameters
- Annotate all parameters
- (On non-functions, annotate all parameters without defaults, and 0 of those with defaults from right to left; you can do this with types only on functions, and have to allow a deny-by-default future compatibility warning; I'm not going to dig into every nuance (but there are more details here))
You can choose a bullet out of each list independently.
The FLS document is talking about things like meeting bounds. It appears to be talking about more generic parameter positions than just functions, such as struct turbofish, impl headers, field definitions, and the like. They use the term "parameter initializers" for defaulted parameters. They say
A lifetime argument is conformant with a lifetime parameter when it outlives the lifetime specified by the lifetime parameter.
but there isn't always a lifetime specified by a parameter, and when there is, "outlives" is not always sufficient due to things like variance.
They do talk about order sensibly...
All lifetime arguments come first, followed by constant arguments and type arguments in the order defined by the generic parameters, followed by binding arguments
...though binding arguments are only applicable in bounds and dyn types I believe. And context effects what is allowed to have defaulted parameters and how elision works.
Their description of what can be elided, e.g.
Any remaining generic parameters without corresponding conformant generic arguments are constant parameters with constant parameter initializers, lifetime parameters with either inferred lifetime arguments or elided lifetimes, type parameters with type parameter initializers or inferred type arguments,
...is incomplete as, like the demonstration earlier in this comment shows; once you annotate some lifetimes or parameters, you cannot always elide the rest. Or maybe it's implied by the section as a whole, and my legalese is not strong enough... as it is pretty heavy on the legalese.
So on the whole I don't think it's necessarily a great source of authority for this topic.