Isso agora é abordado na segunda edição de The Rust Programming Language . No entanto, vamos mergulhar um pouco mais além.
Vamos começar com um exemplo mais simples.
Quando é apropriado usar um método de característica?
Existem várias maneiras de fornecer vinculação tardia :
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)
}
}
Desconsiderando qualquer estratégia de implementação / desempenho, os dois trechos acima permitem que o usuário especifique de forma dinâmica como hello_worlddeve se comportar.
A única diferença (semanticamente) é que a traitimplementação garante que, para um determinado tipo que Timplementa o trait, hello_worldsempre terá o mesmo comportamento, enquanto ostruct implementação permite ter um comportamento diferente por instância.
Se usar um método é apropriado ou não depende do caso de uso!
Quando é apropriado usar um tipo associado?
De maneira semelhante aos traitmétodos acima, um tipo associado é uma forma de vinculação tardia (embora ocorra na compilação), permitindo que o usuário do traitespecifique para uma determinada instância que tipo substituir. Não é a única maneira (daí a questão):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Ou:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
São equivalentes à ligação tardia dos métodos acima:
- o primeiro reforça que para um dado
Selfexiste um único Returnassociado
- o segundo, em vez disso, permite a implementação
MyTraitde Selfpara múltiplosReturn
Qual forma é mais apropriada depende se faz sentido impor a unicidade ou não. Por exemplo:
Deref usa um tipo associado porque sem unicidade o compilador enlouqueceria durante a inferência
Add usa um tipo associado porque seu autor pensou que dados os dois argumentos haveria um tipo de retorno lógico
Como você pode ver, embora Derefseja um caso de uso óbvio (restrição técnica), o caso de Addé menos claro: talvez faria sentido i32 + i32render um i32ou Complex<i32>dependendo do contexto? No entanto, o autor exerceu seu julgamento e decidiu que não era necessário sobrecarregar o tipo de retorno para acréscimos.
Minha posição pessoal é que não existe uma resposta certa. Ainda assim, além do argumento da unicidade, eu mencionaria que os tipos associados tornam o uso do traço mais fácil, pois diminuem o número de parâmetros que devem ser especificados, então, caso os benefícios da flexibilidade de usar um parâmetro de traço regular não sejam óbvios, I sugerir começar com um tipo associado.
trait/struct MyTrait/MyStructpermite exatamente umimpl MyTrait forouimpl MyStruct.trait MyTrait<Return>permite váriosimpls porque é genérico.Returnpode ser de qualquer tipo. As estruturas genéricas são iguais.