I've yet to figure out why to use associated types instead of generic parameters, and every time I've seen an explanation I nod my head in confusion and carry on.
Associated types serve much the same role as generic parameters in the context of traits, but with the crucial distinction that associated types are specified by the person implementing the trait for their type, whereas generic parameters are specified by the person calling the methods defined by the trait.
In practice you could basically just have generic parameters and not bother with associated types, but it would be much less nice to use these APIs.
Here's an example of how an API would change if Rust didn't have associated types:
let x = [1,2,3]; // an array of i32
let mut y = x.iter(); // an iterator over it
y.next(); // pull an item from the iterator
If Rust didn't have associated types, then that last line would have to look like this:
y.next::<&i32>(); // if Rust didn't have associated types
You can view generic parameters as "inputs", they are controlled by the consumer of the type/trait (you construct a `Vec<String>` because you want to store a bunch of `String` values).
Associated types are "outputs", they are controlled by the impl (`<Vec<String> as IntoIterator>::into_iter()` will always return a `std::vec::IntoIter<String>`, because that is how the trait is implemented for `Vec<T>`).
You can often use generic parameters for "outputs" as well (you could have a world where you had `impl<T> IntoIterator<IntoIter<T>> for Vec<T>`). However, this prevents the compiler from inferring the "output" type since there is no longer a guarantee that it is unique for a given set of "inputs".
A: type level functions from types to (associated) types (i.e. normal generics)
B: type level functions from types to type level functions (either As or Bs) (GAT).
with generic parameters (i.e. higher kinded types, or template template parameters in C++) you could have type level functions that map type functions to (types or type functions), but once you have GATs you can fake them by passing to first order functions a type that has an associated type.