Jest to teraz poruszane w drugiej edycji Języka programowania Rusta . Jednak zanurzmy się trochę dodatkowo.
Zacznijmy od prostszego przykładu.
Kiedy należy stosować metodę cech?
Istnieje wiele sposobów zapewnienia późnego wiązania :
trait MyTrait {
fn hello_word(&self) -> String;
}
Lub:
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)
}
}
Pomijając jakąkolwiek strategię wdrożenia / wydajności, oba powyższe fragmenty pozwalają użytkownikowi dynamicznie określić, jak hello_worldpowinien się zachować.
Jedyną różnicą (semantycznie) jest to, że traitimplementacja gwarantuje, że dla danego typu Timplementacja trait, hello_worldzawsze będzie miała takie samo zachowanie, podczas gdy structimplementacja pozwala na inne zachowanie na podstawie instancji.
To, czy użycie metody jest właściwe, czy nie, zależy od przypadku użycia!
Kiedy należy użyć powiązanego typu?
Podobnie jak w przypadku traitpowyższych metod, skojarzony typ jest formą późnego wiązania (chociaż występuje podczas kompilacji), umożliwiając użytkownikowi traitokreślenie dla danej instancji, który typ ma zastąpić. To nie jedyny sposób (stąd pytanie):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Lub:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
Są równoważne z późnym wiązaniem powyższych metod:
- pierwszy wymusza, że dla danego
Selfjest jeden Returnpowiązany
- druga natomiast pozwala na zaimplementowanie
MyTraitdo Selfdo wielokrotnościReturn
To, która forma jest bardziej odpowiednia, zależy od tego, czy wymuszanie jedności ma sens, czy nie. Na przykład:
Deref używa skojarzonego typu, ponieważ bez unikalności kompilator zwariowałby podczas wnioskowania
Add używa skojarzonego typu, ponieważ jego autor uważał, że biorąc pod uwagę te dwa argumenty, będzie to logiczny typ zwracany
Jak widać, gdy Derefjest oczywiste, USECASE (ograniczenie techniczny), sprawa Addjest mniej jednoznaczne: może to mieć sens dla i32 + i32uzyskując albo i32czy Complex<i32>w zależności od kontekstu? Niemniej jednak autor wywiązał się ze swojego osądu i uznał, że przeładowanie zwracanego typu dla dodatków nie jest konieczne.
Osobiście uważam, że nie ma właściwej odpowiedzi. Mimo wszystko, poza argumentem unicity, wspomniałbym, że powiązane typy ułatwiają korzystanie z cechy, ponieważ zmniejszają liczbę parametrów, które należy określić, więc w przypadku, gdy korzyści płynące z elastyczności stosowania zwykłego parametru cechy nie są oczywiste, ja sugeruj rozpoczęcie od powiązanego typu.
trait/struct MyTrait/MyStructpozwala dokładnie na jedenimpl MyTrait forlubimpl MyStruct.trait MyTrait<Return>zezwala na wieleimpls, ponieważ jest ogólny.Returnmoże być dowolnego typu. Struktury ogólne są takie same.