Why associated type considered as ambigious by compiler?

Hi everyone! When i try to compile this, compiler consider associated type here &ServiceTypesImpl::UserService as ambigious and advice me to use fully qualified syntax to upcast concrete type to trait, so i can fix this, but i cant understand why it considered as ambigious?


pub trait ServicesTypes: Send + Sync {
    type UserService: UserService;
}

struct ServiceTypesImpl {}

impl ServicesTypes for ServiceTypesImpl {
    type UserService = UserServiceImpl;
}

pub trait ServiceRegistry<T>
where
    T: ServicesTypes,
{
    fn user_service(&self) -> &T::UserService;
}

struct ServiceRegistryImpl;

impl ServiceRegistry<ServiceTypesImpl> for ServiceRegistryImpl {
    fn user_service(&self) -> &ServiceTypesImpl::UserService {
        todo!()
    }
}

Compiler's error:

76 |     fn user_service(&self) -> &ServiceTypesImpl::UserService {
   |                                ^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<ServiceTypesImpl as ServicesTypes>::US`

There isn't anything to indicate where the associated type is coming from. If you added another trait with an associated type with the same name, there's now two possibilities:

pub trait AnotherAssociatedType {
    type UserService;
}

impl AnotherAssociatedType for ServiceTypesImpl {
    type UserService = ();
}

help: use fully-qualified syntax
   |
25 |     fn user_service(&self) -> &<ServiceTypesImpl as AnotherAssociatedType>::UserService {
   |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 |     fn user_service(&self) -> &<ServiceTypesImpl as ServicesTypes>::UserService {
   |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You could say the trait declaration has the information to disambiguate this, but impl blocks are supposed to make sense by themselves, for human reasons.

2 Likes

I get it, so i should every time use fully qualified syntaxt for associated types in concrete types impl blocks). Thanks!

Yeah, except when the type is Self and the associated type is from the trait being implemented. Usually you'll just be copying these from the definition anyway.

impl Iterator for Collection {
    type Item = i32;
    fn next(&mut self) -> Option<Self::Item> { todo!() }
                                 ^^^^^^^^^^
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.