admin管理员组文章数量:1278820
I want to define two enums, almost identical except for one method (partial_cmp
).
In a object-oriented language, i would define the second one as a child class of the first one, and simply change this method.
I'm wondering what's the best way to do it in rust.
One idea (below) would be to define a trait with all the common methods with default implementation.
Then, create the two enums and to implement the partial_cmp
for each of them.
I see two problems with this way:
- i need to replicate the enum values definition (in the example i have only three values
A, B, C
so it's still readable but what if i have 20 ?) - most of the common methods will depend on the values of the enum (see
From<char>
method below). For such methods, i need to write the same code twice.
trait MyTrait {
/* common methods with default implementations */
}
// First Enum
#[derive(PartialEq, PartialOrd, Eq, Ord)]
enum MyEnum1 { A, B, C }
impl From<char> for MyEnum1 {
fn from(value: char) -> Self {
match value {
'A' => Self::A,
'B' => Self::B,
'C' => Self::C,
_ => panic!("unexpected char: {value}")
}
}
}
impl MyTrait for MyEnum1 {}
impl PartialOrd for MyEnum2 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
/* partial_cmp for MyEnum1 */
}
}
// Second Enum
#[derive(PartialEq, PartialOrd, Eq, Ord)]
enum MyEnum2 { A, B, C }
impl From<char> for MyEnum2 {
fn from(value: char) -> Self {
match value {
'A' => Self::A,
'B' => Self::B,
'C' => Self::C,
_ => panic!("unexpected char: {value}")
}
}
}
impl MyTrait for MyEnum2 {}
impl PartialOrd for MyEnum2 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
/* partial_cmp for MyEnum2 */
}
}
I want to define two enums, almost identical except for one method (partial_cmp
).
In a object-oriented language, i would define the second one as a child class of the first one, and simply change this method.
I'm wondering what's the best way to do it in rust.
One idea (below) would be to define a trait with all the common methods with default implementation.
Then, create the two enums and to implement the partial_cmp
for each of them.
I see two problems with this way:
- i need to replicate the enum values definition (in the example i have only three values
A, B, C
so it's still readable but what if i have 20 ?) - most of the common methods will depend on the values of the enum (see
From<char>
method below). For such methods, i need to write the same code twice.
trait MyTrait {
/* common methods with default implementations */
}
// First Enum
#[derive(PartialEq, PartialOrd, Eq, Ord)]
enum MyEnum1 { A, B, C }
impl From<char> for MyEnum1 {
fn from(value: char) -> Self {
match value {
'A' => Self::A,
'B' => Self::B,
'C' => Self::C,
_ => panic!("unexpected char: {value}")
}
}
}
impl MyTrait for MyEnum1 {}
impl PartialOrd for MyEnum2 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
/* partial_cmp for MyEnum1 */
}
}
// Second Enum
#[derive(PartialEq, PartialOrd, Eq, Ord)]
enum MyEnum2 { A, B, C }
impl From<char> for MyEnum2 {
fn from(value: char) -> Self {
match value {
'A' => Self::A,
'B' => Self::B,
'C' => Self::C,
_ => panic!("unexpected char: {value}")
}
}
}
impl MyTrait for MyEnum2 {}
impl PartialOrd for MyEnum2 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
/* partial_cmp for MyEnum2 */
}
}
Share
Improve this question
asked Feb 23 at 21:18
SPHSPH
5103 silver badges7 bronze badges
1 Answer
Reset to default 1Composition is the easiest way to implement something like subclassing in Rust. That allows you to avoid duplicating the cases, but it does have the downside that you can't directly construct MyEnum2
the same way you would with an enum
. You can also avoid duplicating a lot of the code in your other impls by delegating the implementation to MyEnum1
, but you do still have to write the impls, unlike with a subclass.
The standard library actually has some wrapper types like Reverse
that exist purely to modify standard trait impls of the wrapped type. They work exactly this way (although you usually don't keep those types around for anything except the comparison.
use std::cmp::Ordering;
trait MyTrait {
/* common methods with default implementations */
}
// First Enum
#[derive(PartialEq, Eq)]
enum MyEnum1 {
A,
B,
C,
}
impl From<char> for MyEnum1 {
fn from(value: char) -> Self {
match value {
'A' => Self::A,
'B' => Self::B,
'C' => Self::C,
_ => panic!("unexpected char: {value}"),
}
}
}
impl MyTrait for MyEnum1 {}
impl PartialOrd for MyEnum1 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
todo!("partial_cmp for MyEnum1")
}
}
// Second Enum
#[derive(PartialEq, Eq)]
struct MyEnum2(MyEnum1);
impl From<char> for MyEnum2 {
fn from(value: char) -> Self {
// delegate to `MyEnum1`'s impl, wrapping it in `MyEnum2`
Self(From::from(value))
}
}
impl MyTrait for MyEnum2 {}
impl PartialOrd for MyEnum2 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
todo!("partial_cmp for MyEnum2")
}
}
If you really want to be able to construct MyEnum2
the same way you would MyEnum1
, you can add const
s for the enum cases (iirc this is how enum cases without fields are implemented under the hood)
impl MyEnum2 {
const A: MyEnum2 = MyEnum2(MyEnum1::A);
}
本文标签: rustAvoid code replication when defining two almost identical enumsStack Overflow
版权声明:本文标题:rust - Avoid code replication when defining two almost identical enums - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741302043a2371145.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论