Reputation: 21
I am learning rust and have a hard time to change my c++ architecture mindset into the rust architecture world. Currently I struggle defining common functionality for traits to avoid code duplication.
Problem
trait CommonFunctionality {
fn common_method(&self) -> i32;
}
trait SpecialFunctionality1 : CommonFunctionality {
fn special_foo(&self) -> i32; // defined in actual implementations
}
impl<T: SpecialFunctionality1> CommonFunctionality for T {
fn common_method(&self) -> i32 {self.special_foo()}
}
trait SpecialFunctionality2 : CommonFunctionality {
fn special_bar(&self) -> i32; // defined in actual implementations
}
impl<T: SpecialFunctionality2> CommonFunctionality for T {
fn common_method(&self) -> i32 {self.special_bar()}
}
I don't want to implement the CommonFunctionality
for every actual struct that I write. So I had the idea using a super-trait in a library. Every actual struct implementation that implements SpecialFunctionality1
or SpecialFunctionality2
could get the CommonFunctionality
without any additional code. Unfortunately this does not compile.
The compiler throws: error[E0119]: conflicting implementations of trait 'CommonFunctionality'
I found similar problem descriptions, but their root cause was that the types where not distinguishable and here impl<T: SpecialFunctionality1>
and impl<T: SpecialFunctionality2>
are clearly different types (to my understanding).
When commenting out the last three lines everything compiles.
So, can anyone explain why this is not compile-able? Or, has someone an advice for coding a better pattern?
Inefficient Workaround
What does work is, when I implement the CommonFunctionality
only for one of the specialized traits.
The traits in my library could look then like this:
trait CommonFunctionality {
fn common_method(&self) -> i32;
}
trait SpecialFunctionality1 : CommonFunctionality {
fn special_foo(&self) -> i32;
}
impl<T: SpecialFunctionality1> CommonFunctionality for T {
fn common_method(&self) -> i32 {self.special_foo()}
}
trait SpecialFunctionality2 : CommonFunctionality {
fn special_bar(&self) -> i32;
}
While an actual struct implementation of SpecialFunctionality1
is completely decoupled of the CommonFunctionality
(while still providing it):
struct MyStruct1 {
}
impl SpecialFunctionality1 for MyStruct1 {
fn special_foo(&self) -> i32 {46}
}
The actual struct implementation of SpecialFunctionality2
is completely coupled to the CommonFunctionality
:
struct MyStruct2 {
}
impl SpecialFunctionality2 for MyStruct1 {
fn special_bar(&self) -> i32 {36}
}
impl CommonFunctionality for MyStruct2 {
fn common_method(&self) -> i32 {self.special_bar()}
}
Now the last three lines of code need to be implemented in every actual struct that derives functionality from SpecialFunctionality2
. This is inefficient implementation effort. And worse: whenever CommonFunctionality
changes, I have to rework all structs.
Upvotes: 2
Views: 235
Reputation: 9647
Nothing prevents a new trait or concrete type FooBar
from implementing both traits SpecialFunctionality1
and SpecialFunctionality2
.
In that situation, which specialization should the compiler pick? The one that occurs because FooBar
implements ...1
or the one that occurs because FooBar
implements ...2
?
That's where the error comes from.
@john-kugelman already gave some ideas: Helper methods and enums.
There's often a design decision you can make between enums and traits that depend on whether your types are more likely to be exhaustive and controlled by you, as the library's author, or by a user.
If the situation I mentioned above, that a struct implements both of those special functionality traits, should never actually occur, an enum sounds just about right.
In other situations, the way to get code reuse is via the #[derive]
macro, but I don't have any experience implementing that, and according to @Silvio Mayolo it's tricky and messy.
Upvotes: 2