Как я могу реализовать Borrow для универсального контейнера в случае использования связанных типов? - PullRequest
0 голосов
/ 27 октября 2018

Я хотел бы реализовать Borrow для UserFriendlyDataStructure, чтобы обеспечить доступ к полю internal_data внутри функции, которая должна быть независимой от поставщика данных. Тип поля internal_data определяется типом, связанным с признаком TraitA. Обратите внимание, что черта Sealed гарантирует, что ни одна из этих черт здесь не может быть реализована другими клетями; это функциональность, которую я строго предоставляю. Кроме того, тип TraitA::Data ограничен пустой чертой DataTrait для предотвращения использования UserFriendlyDataStructure в качестве этого типа.

Следующий пример объясняет лучше всего:

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);
    }
}

К сожалению, компилятор жалуется:

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;

Есть ли способ достичь того, что я пытаюсь сделать?

1 Ответ

0 голосов
/ 27 октября 2018

Компилятор можно заставить принять код, введя избыточный параметр второго типа, который должен быть идентичен 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
    }
}

Я не знаю, почему это работает, а просто ограничение A::Data: DataTrait - нет. Я думаю, что компилятор должен принять обе версии.

( Полный код на детской площадке )

Редактировать: Тот факт, что нам нужен избыточный тип D в вышеприведенном коде, кажется недостатком текущей реализации компилятора , и, надеюсь, будет решен после эксперимента механизм вывода типов chalk интегрируется в компилятор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...