Afshin Mehrabani
Afshin Mehrabani

Reputation: 34929

Receiving error[E0276]: impl has stricter requirements than trait with generics

I'm trying to define a trait to compare two given parameters and return a Result based on the implementation. It has two generics but they can be anything when we implement it:

pub trait Assert<L: Any + Debug> {
    fn compare<R: Any + Debug>(self, target: R) -> AssertResult;
}

Now, when I want to implement equal, I will do:

pub struct Equal<L> {
    expected: L,
}

impl<L> Equal<L> {
    pub fn new(expected: L) -> Equal<L> {
        Equal { expected: expected }
    }
}

impl<L: 'static + fmt::Debug> Assert<L> for Equal<L> {
    fn compare<R: PartialEq<L> + fmt::Debug>(self, target: R) -> AssertResult {
        if target == self.expected {
            Ok(())
        } else {
            Err(format!(
                "Expected {:?}, received {:?}",
                self.expected,
                target
            ))
        }
    }
}

Link to playground.

I want to be able to accept anything as L and R but restrict them in the implementation. I'm not sure if this is the correct approach though.

Upvotes: 4

Views: 3903

Answers (1)

trent
trent

Reputation: 27915

The L in Assert<L> is unused by the trait, so it serves no purpose; I'm going to ignore it. Without that complication, here's what you wrote:

pub trait Assert {
    fn compare<R: Any + Debug>(self, target: R) -> AssertResult;
}

This is a contract that says, "When a type implements Assert, it shall provide a method compare that can be called with an argument of any type implementing Any and Debug."

impl<L: 'static + fmt::Debug> Assert for Equal<L> {

This is an agreement to implement the Assert contract for Equal<L> where L is any type that is 'static and implements Debug.

    fn compare<R: PartialEq<L> + fmt::Debug>(self, target: R) -> AssertResult {

This a violation of the Assert contract, which said: "shall provide a method compare that can be called with an argument of any type implementing Any and Debug." The compare method you provided does not satisfy those conditions, but can only be called with an argument of a type that implements PartialEq<L> and Debug.

Or, more concisely: You signed a contract that said compare<R: Any + Debug> and tried to deliver a product that was actually compare<R: PartialEq<L> + fmt::Debug>, and the compiler got mad, because you weren't upholding your end of the contract.


How to fix it?

One possible correction (that you are already aware of) looks like this:

pub trait Assert<R> {
    fn compare(self, target: R) -> AssertResult;
}

A contract that says, "A type that implements Assert<R> (where R is any type) shall provide a method compare that can be called with an argument of type R."

impl<L: fmt::Debug, R: PartialEq<L> + fmt::Debug> Assert<R> for Equal<L> {

An agreement to implement Assert<R> for Equal<L> as long as L implements Debug and R implements PartialEq<L> and Debug.

    fn compare(self, target: R) -> AssertResult {

The fulfillment of the Assert contract: a method compare that can be called on an argument of type R. The body of this method may depend on the knowledge that R: PartialEq<L>, since that's in the terms of its "contract".

The combination of the trait and the impl establish the terms of the contract that the impl must fulfill. The first thing you wrote establishes a particular set of terms, and then attempts to deliver a product (the compare method) that doesn't satisfy those terms. The fix outlined above modifies the terms to include all the conditions needed to write the compare method.

Upvotes: 7

Related Questions