Ceci est maintenant abordé dans la deuxième édition de The Rust Programming Language . Cependant, plongeons un peu en plus.
Commençons par un exemple plus simple.
Quand est-il approprié d'utiliser une méthode de trait?
Il existe plusieurs façons de fournir une liaison tardive :
trait MyTrait {
fn hello_word(&self) -> String;
}
Ou:
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)
}
}
Indépendamment de toute stratégie de mise en œuvre / performance, les deux extraits ci-dessus permettent à l'utilisateur de spécifier de manière dynamique comment hello_worlddoit se comporter.
La seule différence (sémantiquement) est que l' traitimplémentation garantit que pour un type donné Timplémentant le trait, hello_worldaura toujours le même comportement alors que lestruct implémentation permet d'avoir un comportement différent sur une base par instance.
Que l'utilisation d'une méthode soit appropriée ou non dépend du cas d'utilisation!
Quand est-il approprié d'utiliser un type associé?
De la même manière que les traitméthodes ci-dessus, un type associé est une forme de liaison tardive (bien qu'elle se produise lors de la compilation), permettant à l'utilisateur de traitspécifier pour une instance donnée le type à remplacer. Ce n'est pas le seul moyen (d'où la question):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Ou:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
Sont équivalents à la liaison tardive des méthodes ci-dessus:
- le premier impose que pour un donné
Selfil y ait un seulReturn associé
- le second, au contraire, permet de mettre en œuvre
MyTraitpour Selfde multiplesReturn
La forme la plus appropriée dépend de la pertinence d'appliquer l'unicité ou non. Par exemple:
Deref utilise un type associé car sans unicité, le compilateur deviendrait fou lors de l'inférence
Add utilise un type associé car son auteur pensait que compte tenu des deux arguments, il y aurait un type de retour logique
Comme vous pouvez le voir, s'il Derefs'agit d'un cas d'utilisation évident (contrainte technique), le cas de Addest moins clair: peut-être serait-il logique i32 + i32de céder l'un i32ou l' autre ou Complex<i32>selon le contexte? Néanmoins, l’auteur a exercé son jugement et a décidé qu’il n’était pas nécessaire de surcharger le type de retour pour les ajouts.
Ma position personnelle est qu'il n'y a pas de bonne réponse. Néanmoins, au-delà de l'argument d'unicité, je mentionnerais que les types associés facilitent l'utilisation du trait car ils diminuent le nombre de paramètres à spécifier, donc au cas où les avantages de la flexibilité de l'utilisation d'un paramètre de trait régulier ne sont pas évidents, je suggérez de commencer par un type associé.
trait/struct MyTrait/MyStructautorise exactement unimpl MyTrait forouimpl MyStruct.trait MyTrait<Return>autorise plusieursimpls car il est générique.Returnpeut être de n'importe quel type. Les structures génériques sont les mêmes.