【问题标题】:How can I implement Borrow for a generic container in the case of the use of associated types?在使用关联类型的情况下,如何为通用容器实现 Borrow?
【发布时间】:2018-10-30 00:51:46
【问题描述】:

我想为 UserFriendlyDataStructure 实现 Borrow 以提供对函数中的 internal_data 字段的访问,该函数应该与数据提供者无关。 internal_data 字段的类型由关联到 trait TraitA 的类型决定。请注意,Sealed 特征确保此处的这些特征都不能被其他 crate 实现;这是我严格提供的功能。此外,TraitA::Data 类型受空 trait 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;

有没有办法实现我想要做的事情?

【问题讨论】:

  • 看起来确实是同一个问题。我不太高兴它需要使用专业化来解决,因为这还不稳定。我看到的替代方法是为所有实现TraitA 的类型手动编写实现(这不是很灵活),或者使用AsRef 而不是Borrow(在我的情况下由于其他原因不是最佳的) .
  • 是否有任何合理的特征限制可以应用于Data?如果您可以限制它使其不能是UserFriendlyDataStructure,则该错误应该得到解决。
  • Data 确实允许任意类型。由于我控制了哪些类型实现了TypeA(这是一个永远不应该有外部实现的内部特征),我可以创建一个绑定到Data 的人工特征(例如一个空特征)。如果可行,我会尝试并写一个答案。
  • 不幸的是,添加绑定到Data 的特征(通过TraitA)不起作用。我可以通过尝试以这种方式解决问题来更新原始问题或添加答案。我该怎么办?

标签: rust


【解决方案1】:

可以通过引入一个冗余的第二类型参数来诱使编译器接受代码,该参数被限制为与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 不会。我认为编译器应该接受这两个版本。

(Full code on the playground)

编辑:在上面的代码中我们需要冗余类型D 的事实似乎是shortcoming of the current compiler implementation,并且一旦集成了实验类型推理引擎chalk,有望得到解决在编译器中。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-17
  • 2022-11-19
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多