Questo è ora toccato nella seconda edizione di The Rust Programming Language . Tuttavia, tuffiamoci un po 'in più.
Cominciamo con un esempio più semplice.
Quando è appropriato utilizzare un metodo dei tratti?
Esistono diversi modi per fornire l' associazione tardiva :
trait MyTrait {
fn hello_word(&self) -> String;
}
O:
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)
}
}
Ignorando qualsiasi strategia di implementazione / prestazione, entrambi gli estratti sopra consentono all'utente di specificare in modo dinamico come hello_world
deve comportarsi.
L'unica differenza (semanticamente) è che l' trait
implementazione garantisce che per un dato tipo che T
implementa il trait
, hello_world
avrà sempre lo stesso comportamento mentre l' struct
implementazione consente di avere un comportamento diverso per istanza.
Se l'utilizzo di un metodo è appropriato o meno dipende dal caso d'uso!
Quando è opportuno utilizzare un tipo associato?
Analogamente ai trait
metodi precedenti, un tipo associato è una forma di associazione tardiva (sebbene si verifichi alla compilazione), che consente all'utente di trait
specificare per una data istanza quale tipo sostituire. Non è l'unico modo (quindi la domanda):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
O:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
Sono equivalenti al binding tardivo dei metodi di cui sopra:
- il primo impone che per un dato
Self
ci sia un solo Return
associato
- il secondo, invece, consente di attuazione
MyTrait
per Self
per moltepliciReturn
Quale forma sia più appropriata dipende dal fatto che abbia senso rafforzare o meno l'unicità. Per esempio:
Deref
usa un tipo associato perché senza unicità il compilatore impazzirebbe durante l'inferenza
Add
usa un tipo associato perché il suo autore pensava che dati i due argomenti ci sarebbe stato un tipo di ritorno logico
Come puoi vedere, mentre Deref
è un caso d'uso ovvio (vincolo tecnico), il caso di Add
è meno netto: forse avrebbe senso i32 + i32
che cedesse i32
o a Complex<i32>
seconda del contesto? Tuttavia, l'autore ha esercitato il proprio giudizio e ha deciso che non era necessario sovraccaricare il tipo di ritorno per le aggiunte.
La mia posizione personale è che non esiste una risposta giusta. Tuttavia, al di là dell'argomento dell'unicità, vorrei menzionare che i tipi associati facilitano l'uso del tratto poiché diminuiscono il numero di parametri che devono essere specificati, quindi nel caso in cui i vantaggi della flessibilità dell'utilizzo di un parametro di tratto regolare non siano ovvi, io suggerire di iniziare con un tipo associato.
trait/struct MyTrait/MyStruct
consente esattamente unoimpl MyTrait for
oimpl MyStruct
.trait MyTrait<Return>
consente più messaggi di postaimpl
elettronica perché è generico.Return
può essere di qualsiasi tipo. Gli struct generici sono gli stessi.