Reputation: 34929
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
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.
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