I have a trait with an associated type and a generic struct::
trait Generator { type Foo; fn generate(&self) -> Self::Foo; } struct Baz<A, B> where A: Generator, { generator: A, // will be some struct implementing Generator, but the exact type will vary vec: Vec<B>, // Each element will be A::Foo }
I want to generate
and put it into my vector:
impl<A: Generator, B> Baz<A, B> { fn addFoo(&mut self) { self.vec.push(self.generator.generate()); } }
Uh-oh! Compile error:
error[E0308]: mismatched types --> src/main.rs:16:27 | 16 | self.vec.push(self.generator.generate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found associated type | = note: expected type `B` found type `<A as Generator>::Foo`
Fair enough, I must explain to the compiler that B
is the same as A::Foo
; let's try with where
:
impl<A: Generator, B> Baz<A, B> where A::Foo = B, {
which doesn't help:
error: equality constraints are not yet supported in where clauses (#20041) --> src/main.rs:16:5 | 16 | A::Foo = B, | ^^^^^^^^^^
Hmm, no equals. Maybe I can do this with the colon operator instead?
impl<A: Generator, B> Baz<A, B> where B: A::Foo, {
error[E0405]: cannot find trait `Foo` in `A` --> src/main.rs:16:11 | 16 | B: A::Foo, | ^^^ not found in `A`
Nope, now it's complaining about A
. Maybe I should say Generator
?
impl<A: Generator, B> Baz<A, B> where B: Generator::Foo, {
error[E0404]: expected trait, found associated type `Generator::Foo` --> src/main.rs:16:8 | 16 | B: Generator::Foo, | ^^^^^^^^^^^^^^ not a trait
Well good work, compiler — it's not a trait; it's an associated type, but that doesn't tell me how to write a where clause that matches it.
What is an associated type? An associated type can be seen as a replacement of a specific type within a protocol definition. In other words: it's a placeholder name of a type to use until the protocol is adopted and the exact type is specified.
If you declare an associated type in a protocol, that type will become generic over different implementations of the protocol, but each conforming class will need to assign a concrete type to that associate type as a typealias (which can be done implicitly by declaring all variables/functions using the associated type ...
GATs (generic associated types) were originally proposed in RFC 1598. As said before, they allow you to define type, lifetime, or const generics on associated types. If you're familiar with languages that have "higher-kinded types", then you could call GATs type constructors on traits.
Swift Generics allows us to create a single function and class (or any other types) that can be used with different data types. This helps us to reuse our code.
I must explain to the compiler that
B
is the same asA::Foo
There is a special syntax for it:
impl<A, B> Baz<A, B> where A: Generator<Foo = B>, { fn add_foo(&mut self) { self.vec.push(self.generator.generate()); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With