Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I bound a trait with a supertrait that uses the trait's associated type as a parameter?

Tags:

rust

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.

like image 821
Jean Avatar asked Mar 04 '16 15:03

Jean


People also ask

What is an associated type?

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.

What are generic associated types?

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.

What are associated types in Rust?

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.

What is trait in rust?

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++.


1 Answers

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;
}
like image 186
Matthieu M. Avatar answered Sep 22 '22 11:09

Matthieu M.