If you have a `trait A { type B; ... }` then for any type T you can implement one specific A with one specific B.
But if you have `trait A<B> { ... }` then you can have a different implementation of A for every disjoint type B.
Then if you have `trait A<B> { type C; ... }` you can have one specific C for every possible disjoint B.
So with associated types you can express additional constraints/semantics which you can not express with only "classic" generics.
Furthermore the compiler can rely on this constraints for e.g. type inference.
If you have a `trait A { type B; ... }` then for any type T you can implement one specific A with one specific B.
But if you have `trait A<B> { ... }` then you can have a different implementation of A for every disjoint type B.
Then if you have `trait A<B> { type C; ... }` you can have one specific C for every possible disjoint B.
So with associated types you can express additional constraints/semantics which you can not express with only "classic" generics.
Furthermore the compiler can rely on this constraints for e.g. type inference.