In Java-lingo, I have an interface R, an interface RT extends R (where RT implements all of R) and a bunch of other classes that all implement RT.
Transitioning to Rust I ended up with two traits
trait R { ... }
trait RT { ... }
where RT is a "subtrait" of R:
impl R for X where X: RT { ... }
Following that I have a bunch of structs, all of which implement RT:
struct RV { ... }
impl RT for RV { ... }
struct I { ... }
impl RT for I { ... }
struct U { ... }
impl RT for U { ... }
// ...
So far so good.
Now I want all of these structs to be comparable to each other, on the basis that all of them implement RT.
In Java I would change RT to
interface RT extends R, Comparable<RT>
and add a default implementation for equals and compareTo.
In Rust I have no idea if or how this could be approached.
I could say trait RT: PartialEq, but that would only make one implementation comparable with itself (RV == RV, but not RV == U).
My next idea was to add blanket implementations for every struct:
impl PartialEq<RV> for X where X: RT
impl PartialEq<I> for X where X: RT
// ...
I understand why this isn't allowed, however I'm still stuck with my initial problem.
I can't cast the values for comparison (RV as RT == U as RT) because RT can't be made into an object.
I could manually implement PartialEq<T> for every combination of structs but that would be a lot of duplication.
I considered using a macro to generate all the different implementations, but that feels so much like brute-forcing, that I question the initial design of my program.
How do I make all the different structs comparable to each other?
That pattern often arises in Java to emulate tagged unions, which are missing from the Java language. In Rust, unless you are writing a library whose users may need to define new implementations of RT, I suspect you’ll be happier with an enum instead of a trait object:
#[derive(PartialEq, Eq)]
enum AnyRT {
RV(RV),
I(I),
U(U),
}
impl RT for AnyRT {
fn foo(&self, ...) {
match self {
AnyRT::RV(rv) => rv.foo(...),
AnyRT::I(i) => i.foo(...),
AnyRT::U(u) => u.foo(...),
}
}
}
Depending on your application, you may then find that you don’t need the RT trait and/or the separate RV, I, U structs at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With