Зараз це торкнулося у другому випуску Мова програмування "Іржа" . Однак давайте зануримось трохи на додачу.
Почнемо з більш простого прикладу.
Коли доцільно використовувати метод ознаки?
Існує кілька способів забезпечити пізнє зв'язування :
trait MyTrait {
fn hello_word(&self) -> String;
}
Або:
struct MyTrait<T> {
t: T,
hello_world: fn(&T) -> String,
}
impl<T> MyTrait<T> {
fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;
fn hello_world(&self) -> String {
(self.hello_world)(self.t)
}
}
Нехтуючи будь-якою стратегією впровадження / продуктивності, обидві витяги вище дозволяють користувачеві динамічно визначати, як hello_worldслід вести себе.
Єдина відмінність (семантично) полягає в тому, що traitреалізація гарантує, що для даного типу, що Tреалізує trait, hello_worldзавжди матиме однакову поведінку, тоді як structреалізація дозволяє мати іншу поведінку на основі екземпляра.
Чи підходить використання методу, чи ні, залежить від використання!
Коли доцільно використовувати пов'язаний тип?
Аналогічно traitвищевказаним методам, асоційований тип - це форма пізнього зв’язування (хоча це відбувається при компіляції), що дозволяє користувачеві traitвказувати для даного примірника, який тип підміняти. Це не єдиний спосіб (таким чином, питання):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Або:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
Еквівалентні пізньому зв’язуванню вищезазначених способів
- перший примушує стверджувати, що для даного
Selfє один Returnасоційований
- другий, замість цього, дозволяє реалізувати
MyTraitдля Selfдля кількохReturn
Яка форма є більш підходящою, залежить від того, чи є сенс здійснювати єдиність чи ні. Наприклад:
Deref використовує асоційований тип, оскільки без єдиності компілятор зійшов би з розуму під час виводу
Add використовує асоційований тип, оскільки його автор вважав, що з урахуванням двох аргументів буде логічний тип повернення
Як бачите, Derefсправа очевидного використання (технічне обмеження) випадок Addє менш чітким: можливо, це мало б сенс i32 + i32поступатися i32або Complex<i32>залежно від контексту? Тим не менш, автор здійснив своє судження і вирішив, що перевантажувати тип повернення для доповнень не потрібно.
Моя особиста позиція полягає в тому, що правильної відповіді немає. І все-таки, поза аргументом unicity, я хотів би зазначити, що асоційовані типи полегшують використання ознаки, оскільки вони зменшують кількість параметрів, які необхідно вказати, тому у випадку, якщо переваги гнучкості використання звичайного параметра ознаки не очевидні, я запропонуйте почати з асоційованого типу.
trait/struct MyTrait/MyStructдозволяє рівно одинimpl MyTrait forабоimpl MyStruct.trait MyTrait<Return>дозволяє кількаimpls, тому що це загальне.Returnможе бути будь-якого типу. Родові структури однакові.