we have a bunch of APIs for <Type as Trait<T, U>> data within the compiler. We represent those as three arguments:
- an id for the trait
- the
Type - the generic parameters
TandUas a slice
so this usually looks like
some_function(arg, id, ty, &[])
sometimes looks like
some_function(arg, id, ty, &[first_param, second_param])
and in a few rare cases like
some_function(arg, id, ty, substs)
because we already got that slice as a finished value.
Now... by the power of impl IntoIterator<Item = GenericArg> I present to you
some_function(arg, id, ty, [first_param, second_param])
(note the lack of an ampersand). And the finished variable mode still works
But... if we're being technically correct, then the self-type is part of the generic parameter list (you just can't write that out as a Rust developer). And in fact this is what some_function does internally. So, let's see what happens if we switch to a single list and remove the self type argument:
some_function(arg, id, [ty.into()]) // yay!
some_function(arg, id, [ty.into(), first_param, second_param])
some_function(arg, id, [ty.into()].into_iter().chain(substs))
The last one is a bit icky, but it's a literal handful, compared with the dozens of the others.
So... what's up with that .into()? Well, the self type must be a type, but generic params could also be lifetimes or constants. So we didn't need it before, because we did the Into inside some_function. But wait, we can just add more generics:
impl IntoIterator<Item = impl Into<GenericArg>>
and after another round of fiddling with a hundred call sites we get
some_function(arg, id, [ty]) // double yay
some_function(arg, id, [ty, first_ty, second_ty]) // all call sites used `.into()` to create the params -.-
with only the "existing list" case staying the same.
This all happened inside https://github.com/rust-lang/rust/pull/104533 (touching ~300 lines and removing ~30)
