Вроде бы и верно всё написано, но как-то поставлено с ног на голову.
По-моему автор путает причину и следствие:
TL;DR Первые контролирует вызываемый код, вторые — вызывающий.
Это как раз следствие того как работает система вывода типов в Rust.
Суть отличия, в моем понимании, в том что типаж с ассоциированным типом может быть реализован только один раз для каждого типа. Т.е. это такая вариация обобщенного параметра с намеренными ограничениями - конкретный тип определяется при реализации, но реализация может быть только одна. Конечно, об этом упоминается в статье, но подается как следствие того что типажи с ассоциированными типами контрилуруются вызываемым кодом.
Если типаж с обобщенным параметром реализовать один раз, чтобы не было противоречий при выводе типа, то и вести он себя будет также как типаж с ассоциированным типом. Вот пример с обобщенным типажом Add
:
struct Foo(&'static str);
#[derive(PartialEq, Debug)]
struct Bar(&'static str, i32);
trait Add<RHS, OUT>{
fn add(self, rhs: RHS) -> OUT;
}
impl Add<i32, Bar> for Foo {
fn add(self, rhs: i32) -> Bar {
Bar(self.0, rhs)
}
}
fn main() {
let x = Foo("test");
let y = x.add(42);
assert_eq!(y, Bar("test", 42));
}
Тип переменной у
выводится компилятором без проблем.