Implementing a trait for reference and non reference types causes conflicting implementations

前端 未结 2 658
迷失自我
迷失自我 2021-01-14 02:46

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:



        
相关标签:
2条回答
  • 2021-01-14 03:18

    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).

    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.

    [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 structs and another way for enums: there simply is no way to express it (in current 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, e.g.:

    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. You have to add ?Sized if that's not what you want.

    ² The where T: Clone + 'static clause doesn't matter, because &'a mut T will never be Clone whether T itself is or not.

    0 讨论(0)
  • 2021-01-14 03:32

    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.

    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. 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.

    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

    0 讨论(0)
提交回复
热议问题