简体   繁体   English

如何为实现另一个特征的类型实现特征而不冲突的实现

[英]How to implement trait for types implementing another trait without conflicting implementations

I have a broad base trait.我有一个广泛的基础特征。 Some types only care about subset of its functionality, so I added a subtrait requiring user to implement smaller set of functions.有些类型只关心其功能的子集,所以我添加了一个子特征,要求用户实现更小的功能集。

This code fails:此代码失败:

trait Base<T> {
    fn foo(arg: bool);
}

// Ext is a narrowing of Base requiring user to provide alternative, simpler interface for the same functionality
trait Ext<T>: Base<T> {
    fn bar();
}

// implement Base<T> for all types implementing Ext<T>
impl<T, E> Base<T> for E
where
    E: Ext<T>,
{
    fn foo(arg: bool) {
        Self::bar();
    }
}

struct Data<T>;

// error[E0119]: conflicting implementations of trait `Base<_>` for type `Data<_>`:
impl<T> Base<T> for Data<T> {
    fn foo(arg: bool) {}
}

With following error:出现以下错误:

error[E0119]: conflicting implementations of trait `Base<_>` for type `Data<_>`:
  --> src/lib.rs:22:1
   |
11 | / impl<T, E> Base<T> for E
12 | | where
13 | |     E: Ext<T>,
14 | | {
...  |
17 | |     }
18 | | }
   | |_- first implementation here
...
22 |   impl<T> Base<T> for Data<T> {
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Data<_>`
   |
   = note: downstream crates may implement trait `Ext<_>` for type `Data<_>`

Interestingly, it works when I remove generality over T :有趣的是,当我删除T的一般性时,它会起作用:

trait Base {
    fn foo(arg: bool);
}

// Ext is a narrowing of Base requiring user to provide alternative, simpler interface for the same functionality
trait Ext: Base {
    fn bar();
}

// implement Base for all types implementing Ext
impl<E> Base for E
where
    E: Ext,
{
    fn foo(arg: bool) {
        Self::bar();
    }
}

struct Data;

// works just fine
impl Base for Data {
    fn foo(arg: bool) {}
}

Some other posts on stackoverflow mentioned similar problems, but they generally have issues with foreign traits (the one from standard library). stackoverflow 上的其他一些帖子提到了类似的问题,但它们通常存在外来特征(来自标准库的特征)的问题。 In my case both trait and type are local, therefore orphan rules should not kick in as far as I understand.就我而言,特征和类型都是本地的,因此据我所知,孤儿规则不应该生效。

Basically, error mentions that downstream crates may implement trait 'Ext<_>' for type 'Data<_>' , which isn't true because both Ext and Data would be foreign for those crates.基本上,错误提到downstream crates may implement trait 'Ext<_>' for type 'Data<_>' ,这是不正确的,因为ExtData对于这些板条箱来说都是外来的。

To summarize, my questions are:总而言之,我的问题是:

  1. Why my blanket impl is rejected even though it doesn't seem to be possible for other crates to create a collision.为什么我的一揽子 impl 被拒绝,即使其他 crate 似乎不可能产生碰撞。
  2. Why version without T is not rejected, even though its mostly the same blanket impl?为什么没有T的版本不会被拒绝,即使它几乎是相同的一揽子 impl?
  3. Is there any workaround for this issue?这个问题有什么解决方法吗?

The reason you're seeing the error is that there is a possible collision between impl Base for E and impl Base for Data您看到错误的原因是 impl Base for E 和 impl Base for Data之间可能存在冲突

these are both generics, so in theory I could create my own struct and implement the trait Base<T> as well as the trait Ext<T> .这些都是 generics,所以理论上我可以创建自己的结构并实现特征Base<T>以及特征Ext<T> If I do this, your code would create duplicate implementations for Base<T>::foo since Both impl blocks are implementing Base<T> .如果我这样做,您的代码将为Base<T>::foo创建重复的实现,因为两个impl块都在实现Base<T>

When you remove the T from your code it becomes more specific.当您从代码中删除T时,它会变得更加具体。 You would implement Base for E and Base for Data .您将为E实现Base和为Data实现Base You can see the same error in your own second code example if you add the following如果添加以下内容,您可以在自己的第二个代码示例中看到相同的错误

impl Ext for Data {
    fn bar() {}
}

It's the same basic message.这是相同的基本信息。 The only difference is your first example only provides the possibility of a collision whereas the second (with my addition) actually causes the collision.唯一的区别是你的第一个例子只提供了碰撞的可能性,而第二个(加上我的补充)实际上导致了碰撞。

As for workarounds... Rust traits are not really inheritance so I would say the rusty way to do this would be to have separate traits for each subset of functions you want instead of trying to create a hierarchy of traits.至于解决方法... Rust 特征并不是真正的 inheritance 所以我想说的生锈方法是为您想要的每个函数子集设置单独的特征,而不是尝试创建特征层次结构。

If your program really needs to have the inheritance type traits, then avoid using generics or bound them to concrete types.如果您的程序确实需要具有 inheritance 类型特征,则避免使用 generics 或将它们绑定到具体类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM