Зараз це торкнулося у другому випуску Мова програмування "Іржа" . Однак давайте зануримось трохи на додачу.
Почнемо з більш простого прикладу.
Коли доцільно використовувати метод ознаки?
Існує кілька способів забезпечити пізнє зв'язування :
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>
дозволяє кількаimpl
s, тому що це загальне.Return
може бути будь-якого типу. Родові структури однакові.