# Ambigous associated type

**URL:** <https://internals.rust-lang.org/t/ambigous-associated-type/14048>\
**Category:** Uncategorized\
**Created:** [February 13, 2021, 3:42pm UTC](https://internals.rust-lang.org/t/ambigous-associated-type/14048 "2021-02-13T15:42:44Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Raidwas](https://avatars.discourse-cdn.com/v4/letter/r/c67d28/32.png) [@Raidwas](https://internals.rust-lang.org/u/Raidwas)\
**Post date:** [February 13, 2021, 3:42pm UTC](https://internals.rust-lang.org/t/ambigous-associated-type/14048/1 "2021-02-13T15:42:44Z")

</div>

I am currently writing a compile time [SKI](https://en.wikipedia.org/wiki/SKI_combinator_calculus#SKI_expressions) evaluator. So far it works well but I noticed something interesting and would like to know why it is not possible.

Some basic information: "Evaluation" is implemented by letting the compiler figure out if there exists an type that satisfies some trait bound. The type it has to find describes the evaluation of the SKI expression so always writing it out is out of question for my use case. The `Eval` trait:

```rust
trait Eval<WorkLeft> {
    type Result;
    fn eval(self) -> Self::Result;
}

```

Now for the interesting part. The following code works and shows a simple example of how to evaluate SKI expressions:

```rust
#[test]
fn test_I() {
    let c4 = USize::<4>;
    let identity = Application(Application(S, K), K);
    let applied = Application(identity, c4);
    let evaluated = applied.eval();
    dbg!(evaluated);
}

```

Notice how we can call `applied.eval()` without specifying any types.

Now the next test fails, which tries to do the same as the previous one only using types:

```rust
#[test]
fn test_I2() {
    type C4 = USize<4>;
    type identity = Application<Application<S, K>, K>;
    type applied = Application<identity, C4>;
    type evaluated = applied::Result;
}

```

Here the compiler complains about an `ambigous associated type` on `applied::Result`.

My question: the compiler obviously knows to check for the existence of the type parameter `WorkLeft` _and_ that it is unique (otherwise `test_I` should give a use fully qualified trait syntax error) on function calls, why does rustc not allow accessing associated types from such a trait?

For the interested the full code (180 lines of code including tests):

> **Code**
>
> ```rust
> use std::marker::PhantomData;
> 
> trait Eval<WorkLeft> {
> type Result;
> fn eval(self) -> Self::Result;
> }
> 
> struct Done;
> struct Todo<T>(PhantomData<T>);
> struct TodoApplication<L, R>(PhantomData<L>, PhantomData<R>);
> 
> trait ConstVal {
> type RuntimeType;
> const VAL: Self::RuntimeType;
> }
> 
> #[derive(Copy, Clone)]
> struct S;
> impl Eval<Done> for S {
> type Result = S;
> fn eval(self) -> Self::Result {
> self
> }
> }
> 
> #[derive(Copy, Clone)]
> struct S1<X>(X);
> impl<X> Eval<Done> for S1<X> {
> type Result = S1<X>;
> fn eval(self) -> Self::Result {
> self
> }
> }
> 
> #[derive(Copy, Clone)]
> struct S2<X, Y>(X, Y);
> impl<X, Y> Eval<Done> for S2<X, Y> {
> type Result = S2<X, Y>;
> fn eval(self) -> Self::Result {
> self
> }
> }
> 
> #[derive(Copy, Clone)]
> struct K;
> impl Eval<Done> for K {
> type Result = K;
> fn eval(self) -> Self::Result {
> self
> }
> }
> 
> #[derive(Copy, Clone)]
> struct K1<X>(X);
> impl<X> Eval<Done> for K1<X> {
> type Result = K1<X>;
> fn eval(self) -> Self::Result {
> self
> }
> }
> 
> #[derive(Copy, Clone)]
> struct Application<L, R>(L, R);
> 
> impl<R, TL, TR, X, Y> Eval<TodoApplication<TL, TR>> for Application<Application<X, Y>, R>
> where
> Application<X, Y>: Eval<TL>,
> Application<<Application<X, Y> as Eval<TL>>::Result, R>: Eval<TR>,
> {
> type Result = <Application<<Application<X, Y> as Eval<TL>>::Result, R> as Eval<TR>>::Result;
> fn eval(self) -> Self::Result {
> Application(Application(self.0 .0, self.0 .1).eval(), self.1).eval()
> }
> }
> 
> impl<R, T> Eval<Todo<T>> for Application<S, R>
> where
> S1<R>: Eval<T>,
> {
> type Result = <S1<R> as Eval<T>>::Result;
> fn eval(self) -> Self::Result {
> S1(self.1).eval()
> }
> }
> impl<R, T, X> Eval<Todo<T>> for Application<S1<X>, R>
> where
> S2<X, R>: Eval<T>,
> {
> type Result = <S2<X, R> as Eval<T>>::Result;
> fn eval(self) -> Self::Result {
> S2(self.0 .0, self.1).eval()
> }
> }
> impl<R, T, X, Y> Eval<Todo<T>> for Application<S2<X, Y>, R>
> where
> R: Copy,
> Application<Application<X, R>, Application<Y, R>>: Eval<T>,
> {
> type Result = <Application<Application<X, R>, Application<Y, R>> as Eval<T>>::Result;
> fn eval(self) -> Self::Result {
> Application(
> Application(self.0 .0, self.1),
> Application(self.0 .1, self.1),
> )
> .eval()
> }
> }
> 
> impl<R, T> Eval<Todo<T>> for Application<K, R>
> where
> K1<R>: Eval<T>,
> {
> type Result = <K1<R> as Eval<T>>::Result;
> fn eval(self) -> Self::Result {
> K1(self.1).eval()
> }
> }
> impl<R, T, X> Eval<Todo<T>> for Application<K1<X>, R>
> where
> X: Eval<T>,
> {
> type Result = <X as Eval<T>>::Result;
> fn eval(self) -> Self::Result {
> self.0 .0.eval()
> }
> }
> 
> // Numbers
> #[derive(Copy, Clone, Debug)]
> struct USize<const N: usize>;
> 
> impl<const N: usize> ConstVal for USize<N> {
> type RuntimeType = usize;
> const VAL: Self::RuntimeType = N;
> }
> 
> impl<const N: usize> Eval<Done> for USize<N> {
> type Result = Self;
> fn eval(self) -> Self::Result {
> USize::<N>
> }
> }
> 
> impl<R, T, const N: usize> Eval<Todo<T>> for Application<USize<N>, R>
> where
> R: Eval<T>,
> {
> type Result = Application<USize<N>, <R as Eval<T>>::Result>;
> fn eval(self) -> Self::Result {
> Application(self.0, self.1.eval())
> }
> }
> 
> #[cfg(test)]
> mod test {
> use super::*;
> #[test]
> fn test_K1() {
> type T = Application<K1<USize<1>>, USize<2>>;
> type R = <T as Eval<Todo<Done>>>::Result;
> const n: usize = R::VAL;
> }
> #[test]
> fn test_K() {
> type T = Application<Application<K, USize<1>>, USize<2>>;
> type R = <T as Eval<TodoApplication<Todo<Done>, Todo<Done>>>>::Result;
> const n: usize = R::VAL;
> }
> 
> #[test]
> fn test_I() {
> let c4 = USize::<4>;
> let identity = Application(Application(S, K), K);
> let applied = Application(identity, c4);
> let evaluated = applied.eval();
> dbg!(evaluated);
> }
> 
> #[test]
> fn test_I2() {
> type C4 = USize<4>;
> type identity = Application<Application<S, K>, K>;
> type applied = Application<identity, C4>;
> type evaluated = applied::Result;
> }
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![birkenfeld](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/birkenfeld/32/1150_2.png) [@birkenfeld](https://internals.rust-lang.org/u/birkenfeld)\
**Post date:** [February 13, 2021, 4:15pm UTC](https://internals.rust-lang.org/t/ambigous-associated-type/14048/2 "2021-02-13T16:15:14Z")

</div>

Without looking too closely at the whole code, I think you just need to tell rustc that you _mean_ the trait's associated type:

```rust
type evaluated = <applied as Eval<something>>::Result;

```

This also shows why it is otherwise ambiguous: there could be multiple `impl Eval<T>` for that type for different `T`.

---

<div class="post-metadata">

**Author:** ![Raidwas](https://avatars.discourse-cdn.com/v4/letter/r/c67d28/32.png) [@Raidwas](https://internals.rust-lang.org/u/Raidwas)\
**Post date:** [February 13, 2021, 4:40pm UTC](https://internals.rust-lang.org/t/ambigous-associated-type/14048/3 "2021-02-13T16:40:47Z")

</div>

But the same reasoning could be applied to calling `Eval<T>::eval()` on `applied` in the first example, right? But there rustc somehow figures out we mean `Eval<T>::eval()` for some `T` _and_ checks that there is only a single `T` that fits and finally uses it to call `eval`...

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [May 14, 2021, 4:41pm UTC](https://internals.rust-lang.org/t/ambigous-associated-type/14048/4 "2021-05-14T16:41:46Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
