I have a trait Trait
with an associated type Trait::Associated
. I am trying to bound the trait by requiring that it be indexable by its associated type, as shown here:
use std::ops::Index;
pub trait Trait: Index<Trait::Associated> {
type Associated;
}
However, the compiler complains that the associated type is ambiguous
error[E0223]: ambiguous associated type --> src/main.rs:3:24 | 3 | pub trait Trait: Index<Trait::Associated> { | ^^^^^^^^^^^^^^^^^ ambiguous associated type | = note: specify the type using the syntax `<Type as Trait>::Associated`
I also tried referring to the associated type as Self::Associated
, but then the compiler protests about a cyclic reference between the type and the trait:
error[E0391]: cyclic dependency detected --> src/main.rs:3:1 | 3 | pub trait Trait: Index<Self::Associated> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic reference | note: the cycle begins when computing the supertraits of `Trait`... --> src/main.rs:3:1 | 3 | pub trait Trait: Index<Self::Associated> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which then again requires computing the supertraits of `Trait`, completing the cycle.
Finally, I also tried explicitly implementing Index
for Trait
:
pub trait Trait {
type Associated;
}
impl<T: Trait> Index<T::Associated> for T {
type Output = str;
fn index(&self, associated: T::Associated) -> &'static str {
"sup"
}
}
Unfortunately that fails too:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`) --> src/main.rs:7:1 | 7 | impl<T: Trait> Index<T::Associated> for T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `T` must be used as the type parameter for some local type | = note: only traits defined in the current crate can be implemented for a type parameter
Am I trying to do something unreasonable here? Is there a way of achieving something similar, without having to use generics?
Playground.
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.
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.
Associated types are a powerful part of Rust's type system. They're related to the idea of a 'type family', in other words, grouping multiple types together. That description is a bit abstract, so let's dive right into an example.
A trait tells the Rust compiler about functionality a particular type has and can share with other types. Traits are an abstract definition of shared behavior amongst different types. So, we can say that traits are to Rust what interfaces are to Java or abstract classes are to C++.
You are close, very close.
The Trait
does not assume that any reference to Trait
in its definition refers to the current type. After all, you could wish to refer to other types also implementing Trait
.
In order to specify that you want a specific type, you should heed the compilers note: use <Type as Trait>::Associated
, where Type
is the current type.
When defining the Trait
, how do you refer to the concrete type for which it will be instantiated? You use Self
!
The solution is:
pub trait Trait: Index<<Self as Trait>::Associated> {
type Associated;
}
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