简体   繁体   English

为引用和非引用类型实现特征会导致实现冲突

[英]Implementing a trait for reference and non reference types causes conflicting implementations

I'm trying to create a trait and provide one implementation for all non-reference types, and another for all reference types. 我正在尝试创建一个特征并为所有非引用类型提供一个实现,并为所有引用类型提供另一个实现。

This fails to compile: 这无法编译:

trait Foo {}
impl<T> Foo for T {}
impl<'a, T> Foo for &'a mut T {}

This fails with the error 这失败了,错误

error[E0119]: conflicting implementations of trait `Foo` for type `&mut _`:
 --> src/main.rs:3:1
  |
2 | impl<T> Foo for T {}
  | -------------------- first implementation here
3 | impl<'a, T> Foo for &'a mut T {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&mut _`

Strangely enough, this works: 奇怪的是,这有效:

trait Bar {}

impl<T> Bar for T
where
    T: Clone,
{}

impl<'a, T> Bar for &'a mut T
where
    T: Clone + 'static,
{}

Why does the version with the Clone constraint work, and how can I make it work without it? 为什么具有Clone约束的版本有效,如何在没有它的情况下使其工作?

As you have learned, a generic T can be anything¹, so the Foo impls overlap (conflict) whenever T in the first impl is &'a mut U , because the second impl also covers that case (when T is U ). 正如您所了解的那样,通用T可以是任何¹,因此只要第一个impl中的T&'a mut UFoo就会重叠(冲突),因为第二个impl也涵盖了这种情况(当TU )。

The Clone version works simply because &mut references never implement Clone , so there's no overlap between T where T: Clone and &'a mut T .² If you try to implement Bar for immutable ( & ) references, you will have a conflict again, because immutable references do implement Clone . Clone版本的工作原理只是因为&mut引用永远不会实现Clone ,因此T where T: Clone之间没有重叠, T where T: Clone&'a mut T T.2。如果你试图为不可变( & )引用实现Bar ,你将再次发生冲突,因为不可变引用确实实现了Clone

[H]ow can I make it work without it? 如果没有它,我可以让它工作吗?

If by "it" you mean one implementation for reference types and another, different one for non-reference types, that's not possible in Rust for the same reason you can't implement a trait one way for struct s and another way for enum s: there simply is no way to express it (in current Rust). 如果用“it”表示引用类型的一个实现,而另一个用于非引用类型的实现,则在Rust中不可能出于同样的原因,你不能为struct s实现一种特性,而另一种方式用于enum :根本没有办法表达它(在当前的Rust中)。

One common pattern that might work for you is implementing your trait individually for whatever non-reference types you need, and then adding a "blanket impl" that covers any reference to a type for which the trait is already implemented, eg: 一个可能适合您的常见模式是为您需要的任何非引用类型单独实现您的特征,然后添加“覆盖impl”,其涵盖对已经实现特征的类型的任何引用,例如:

impl Foo for u32 { ... }
impl Foo for i32 { ... }
impl<'a, T> Foo for &'a T where T: Foo + 'a { ... }
impl<'a, T> Foo for &'a mut T where T: Foo + 'a { ... }

¹ Well, anything that is Sized , at least. ¹嗯,任何Sized东西,至少。 You have to add ?Sized if that's not what you want. 你必须添加?Sized如果这不是你想要的。

² The where T: Clone + 'static clause doesn't matter, because &'a mut T will never be Clone whether T itself is or not. ² where T: Clone + 'static子句无关紧要,因为&'a mut T将永远无法Clone无论T本身是否存在。

The reason for the Clone version works is because the types that the trait is being implemented for are no longer conflicting on the implementation. Clone版本工作的原因是因为实现特征的类型不再在实现上发生冲突。

Take the first example and add a default implementation. 以第一个示例为例,添加默认实现。

trait Foo {
    fn hi(&self){
        println!("Hi");
    }
}

And then we implement Foo for all of type T with impl<T> Foo for T {} this actually implements enough for us to use a reference to our types and use the Foo trait. 然后我们impl<T> Foo for T {}使用impl<T> Foo for T {} Foo为所有类型T实现Foo ,这实际上足以让我们使用对类型的引用并使用Foo特征。 For Example: 例如:

fn say_hi<'a>(b: &'a mut Foo){
    b.hi();
}

fn main(){
    let mut five = 5;

    five.hi(); // integer using Foo
    say_hi(&mut five); // &'a mut Foo
}

To answer the second part of you question, you didn't need the second implement of impl<'a,T> Foo for &'a mut T {} because impl<T> Foo for T {} was enough to give you what you were looking for. 为了回答你问题的第二部分,你不需要第二个实现impl<'a,T> Foo for &'a mut T {}因为impl<T> Foo for T {} impl<'a,T> Foo for &'a mut T {} impl<T> Foo for T {}足以给你什么你在找。

Now that we have seen that the first example works without the second implement it starts to make sense that the example using Clone works because you are implementing for a subset of types T that are Clone and a different subset of types &'a mut T that are Clone+static 现在我们已经看到第一个示例在没有第二个实现的情况下工作,它开始有意义,使用Clone的示例有效,因为您正在实现类型T的子集,它们是Clone和不同的类型子集&'a mut T Clone+static

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

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