How can I implement Borrow for a generic container in the case of the use of associated types?

Viewed 248

I would like to implement Borrow for UserFriendlyDataStructure to provide access to the internal_data field within a function that should be agnostic with respect to the data provider. The type of the internal_data field is determined by the type associated to trait TraitA. Note that the Sealed trait ensures that none of these traits here can be implemented by other crates; this is functionality that strictly I provide. Furthermore, the type TraitA::Data is restricted by the empty trait DataTrait to prevent UserFriendlyDataStructure from being used as that type.

The following example explains best:

use std::borrow::Borrow;
use std::marker::PhantomData;

mod private {
    pub trait Sealed {}
}

pub trait DataTrait: private::Sealed {}

pub trait TraitA: private::Sealed {
    type Data: DataTrait;
}

pub struct UserFriendlyDataStructure<A: TraitA> {
    internal_data: A::Data,
    _a: PhantomData<A>,
}

impl<A: TraitA> Borrow<A::Data> for UserFriendlyDataStructure<A> {
    fn borrow(&self) -> &A::Data {
        &self.internal_data
    }
}

pub fn important_function<A: TraitA, T: Borrow<A::Data>>(data: &T) {
    let _internal_data = data.borrow();
    // Do lots of work.
}

#[cfg(test)]
mod tests {
    use super::*;

    pub struct TestData(u32);

    impl super::private::Sealed for TestData {}

    impl DataTrait for TestData {}

    pub struct TestProvider;

    impl super::private::Sealed for TestProvider {}

    impl TraitA for TestProvider {
        type Data = TestData;
    }

    #[test]
    fn basic_test() {
        let ufds: UserFriendlyDataStructure<TestProvider> = UserFriendlyDataStructure {
            internal_data: TestData(100),
            _a: PhantomData::default(),
        };

        important_function::<TestProvider, _>(&ufds);
    }
}

Unfortunately, the compiler complains:

error[E0119]: conflicting implementations of trait `std::borrow::Borrow<UserFriendlyDataStructure<_>>` for type `UserFriendlyDataStructure<_>`:
  --> src/lib.rs:19:1
   |
19 | impl<A: TraitA> Borrow<A::Data> for UserFriendlyDataStructure<A> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T> std::borrow::Borrow<T> for T
             where T: ?Sized;

Is there a way to achieve what I am trying to do?

1 Answers

The compiler can be cajoled into accepting the code by introducing a redundant second type parameter that is constrained to be identical to A::Data:

impl<A, D> Borrow<D> for UserFriendlyDataStructure<A>
where
    A: TraitA<Data = D>,
    D: DataTrait,
{
    fn borrow(&self) -> &A::Data {
        &self.internal_data
    }
}

I don't know why this works, and simply constraining A::Data: DataTrait doesn't. I think the compiler should accept both versions.

(Full code on the playground)

Edit: The fact that we need the redundant type D in the above code appears to be a shortcoming of the current compiler implementation, and is hopefully resolved once the experimental type inference engine chalk gets integrated in the compiler.

Related