What's the difference between a trait's generic type and a generic associated type?

  • A+
Category:Languages

This question is asked before generic associated types are available in Rust, although they are proposed and developed.

My understanding is that trait generics and associated types differ in the number of types which they can bind to a struct.

Generics can bind any number of types:

struct Struct;  trait Generic<G> {     fn generic(&self, generic: G); }  impl<G> Generic<G> for Struct {     fn generic(&self, _: G) {} }  fn main() {     Struct.generic(1);     Struct.generic("a"); } 

Associated types bind exactly 1 type:

struct Struct;  trait Associated {     type Associated;      fn associated(&self, associated: Self::Associated); }  impl Associated for Struct {     type Associated = u32;      fn associated(&self, _: Self::Associated) {} }  fn main() {     Struct.associated(1);     // Struct.associated("a"); // `expected u32, found reference` } 

Generic associated types are a mix of these two. They bind to a type exactly 1 associated generator, which in turn can associate any number of types. Then what is the difference between Generic from the previous example and this generic associated type?

struct Struct;  trait GenericAssociated {     type GenericAssociated;      fn associated(&self, associated: Self::GenericAssociated); }  impl<G> GenericAssociated for Struct {     type GenericAssociated = G;      fn associated(&self, _: Self::GenericAssociated) {} } 

 


What's the difference?

Generic Associate Types (GATs) are associated types which are themselves generic. The RFC starts with a motivating example, emphasis mine:

Consider the following trait as a representative motivating example:

trait StreamingIterator {     type Item<'a>;     fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>; } 

This trait is very useful - it allows for a kind of Iterator which yields values which have a lifetime tied to the lifetime of the reference passed to next. A particular obvious use case for this trait would be an iterator over a vector which yields overlapping, mutable subslices with each iteration. Using the standard Iterator interface, such an implementation would be invalid, because each slice would be required to exist for as long as the iterator, rather than for as long as the borrow initiated by next.

This trait cannot be expressed in Rust as it exists today, because it depends on a sort of higher-kinded polymorphism. This RFC would extend Rust to include that specific form of higher-kinded polymorphism, which is refered to here as associated type constructors. This feature has a number of applications, but the primary application is along the same lines as the StreamingIterator trait: defining traits which yield types which have a lifetime tied to the local borrowing of the receiver type.

Note how the associated type Item has a generic lifetime 'a. Most examples in the RFC use lifetimes, but there's also an example using a generic type:

trait PointerFamily {     type Pointer<T>: Deref<Target = T>;     fn new<T>(value: T) -> Self::Pointer<T>; } 

Note how the associated type Pointer has a generic type T.

Your specific example

what is the difference between Generic from the previous example and this generic associated type

There may be none, and the existence of GATs would not help your case, which does not seem to require an associated type which is itself generic.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: