Search code examples
structrustimplementationspecialization

Specialise function implementation for a struct implementation


I am implementing a struct with a generic bounded to a trait, but that implementation is desirable to feature functions that bound the generic even more. Below is the example:

struct A<T> {
   data: T
}

impl <T: AsRef<[u8]>> A<T> {
    fn test(&self, t: &T) {}
    fn more_bound<S: AsRef<[u8]> + PartialEq>(&self, t: &S) {
        self.test(t);  
    }
}

Playground

I cannot really use a specialization as I don't implement a trait. Neither would I like to define a trait. Are there any other options except changing the signature of test to fn test(&self, t: &impl AsRef<[u8]>) {}? Because such an approach seems to defeat the purpose of generics (in this case).


Solution

  • The compiler throws an error because in more_bound you take an S and then pass it into test, which requires a T. The fact that S and T are both AsRef<[u8]> (and T is weaker than S) is irrelevant since those generics have to match a fixed, concrete type (you promised a &T but gave an &S - who knows what &S is).

    You can simply split the impl into two parts:

    impl<T: AsRef<[u8]>> A<T> {
        fn test(&self, t: &T) {}
    }
    
    impl<T: AsRef<[u8]> + PartialEq> A<T> {
        fn more_bound(&self, t: &T) {
            self.test(t);
        }
    }
    

    The second impl will only be applicable for a T that is AsRef<[u8]> + PartialEq. As this bound guarantees that this T is AsRef<[u8]>, the more_bound method can call test, defined in the first impl.

    If your original goal was to allow more_bound to be called with different types, you'll have to do the type-conversion via AsRef yourself:

    impl <T: AsRef<[u8]>> A<T> {
        // Notice `test`takes the target of `AsRef<[u8]>`
        fn test(&self, t: &[u8]) {}
    
        fn more_bound<S: AsRef<[u8]> + PartialEq>(&self, t: &S) {
            // `S` can be whatever we want, it never "meets" `T`.
            self.test(t.as_ref()); 
        }
    }