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_worlddeve comportarsi.
L'unica differenza (semanticamente) è che l' traitimplementazione garantisce che per un dato tipo che Timplementa il trait, hello_worldavrà sempre lo stesso comportamento mentre l' structimplementazione 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 traitmetodi precedenti, un tipo associato è una forma di associazione tardiva (sebbene si verifichi alla compilazione), che consente all'utente di traitspecificare 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
Selfci sia un solo Returnassociato
- il secondo, invece, consente di attuazione
MyTraitper Selfper 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 + i32che cedesse i32o 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/MyStructconsente esattamente unoimpl MyTrait foroimpl MyStruct.trait MyTrait<Return>consente più messaggi di postaimplelettronica perché è generico.Returnpuò essere di qualsiasi tipo. Gli struct generici sono gli stessi.