You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中避免定义近乎相同枚举时的代码重复方案

Rust中实现仅排序逻辑不同的相似枚举的最优方案

问题背景

需要定义两个近乎完全相同的枚举,仅partial_cmp方法(排序逻辑)存在差异。面向对象语言中通常用子类继承修改方法,但Rust不支持继承,此前尝试的trait方案存在两个痛点:

  • 重复定义枚举值,枚举值数量多时维护成本高
  • 依赖枚举值的公共方法(如From<char>)需要重复编写

可行解决方案

方案1:组合式设计——内部共享枚举+排序策略标记

核心思路是把枚举的公共部分提取为一个内部枚举,再用带排序策略标记的外层结构体包裹,复用内部枚举的所有公共逻辑,仅针对不同策略实现排序。

代码实现

// 共享的内部枚举,包含所有枚举值和公共逻辑
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SharedEnum {
    A,
    B,
    C,
}

// 公共的From<char>实现,只写一次
impl From<char> for SharedEnum {
    fn from(value: char) -> Self {
        match value {
            'A' => Self::A,
            'B' => Self::B,
            'C' => Self::C,
            _ => panic!("unexpected char: {value}"),
        }
    }
}

// 公共方法的trait及实现
trait MyTrait {
    fn some_common_method(&self) -> String;
}

impl MyTrait for SharedEnum {
    fn some_common_method(&self) -> String {
        format!("Handling {:?}", self)
    }
}

// 排序策略标记:零大小类型,仅用于区分排序逻辑,不占用内存
#[derive(Debug, Clone, Copy)]
struct DefaultOrder;
#[derive(Debug, Clone, Copy)]
struct ReverseOrder;

// 带排序策略的外层包装结构体
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct EnumWithOrder<Order>(SharedEnum, std::marker::PhantomData<Order>);

// 转发From<char>到内部枚举
impl<Order> From<char> for EnumWithOrder<Order> {
    fn from(value: char) -> Self {
        Self(SharedEnum::from(value), std::marker::PhantomData)
    }
}

// 转发MyTrait方法到内部枚举
impl<Order> MyTrait for EnumWithOrder<Order> {
    fn some_common_method(&self) -> String {
        self.0.some_common_method()
    }
}

// 为默认排序策略实现PartialOrd和Ord
impl PartialOrd for EnumWithOrder<DefaultOrder> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.0.partial_cmp(&other.0) // 使用内部枚举的自然顺序
    }
}

impl Ord for EnumWithOrder<DefaultOrder> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

// 为自定义排序策略实现PartialOrd和Ord(示例:反转顺序)
impl PartialOrd for EnumWithOrder<ReverseOrder> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        other.0.partial_cmp(&self.0) // 反转自然顺序
    }
}

impl Ord for EnumWithOrder<ReverseOrder> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

使用示例

fn main() {
    let a_default: EnumWithOrder<DefaultOrder> = 'A'.into();
    let b_default: EnumWithOrder<DefaultOrder> = 'B'.into();
    println!("a_default < b_default: {}", a_default < b_default); // 输出true

    let a_reverse: EnumWithOrder<ReverseOrder> = 'A'.into();
    let b_reverse: EnumWithOrder<ReverseOrder> = 'B'.into();
    println!("a_reverse < b_reverse: {}", a_reverse < b_reverse); // 输出false
}

方案2:使用宏自动生成重复代码

如果希望直接得到两个独立的枚举,可通过宏自动生成枚举定义、公共方法实现,仅手动传入不同的partial_cmp逻辑。

代码实现

// 定义生成枚举的宏,将公共逻辑封装在宏内
macro_rules! define_ordered_enum {
    ($enum_name:ident, $cmp_logic:block) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        enum $enum_name {
            A,
            B,
            C,
        }

        impl From<char> for $enum_name {
            fn from(value: char) -> Self {
                match value {
                    'A' => Self::A,
                    'B' => Self::B,
                    'C' => Self::C,
                    _ => panic!("unexpected char: {value}"),
                }
            }
        }

        trait MyTrait {
            fn some_common_method(&self) -> String;
        }

        impl MyTrait for $enum_name {
            fn some_common_method(&self) -> String {
                format!("Handling {:?}", self)
            }
        }

        impl PartialOrd for $enum_name {
            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
                $cmp_logic
            }
        }

        impl Ord for $enum_name {
            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
                self.partial_cmp(other).unwrap()
            }
        }
    };
}

// 生成第一个枚举:使用默认排序逻辑
define_ordered_enum!(MyEnum1, {
    match (self, other) {
        (Self::A, Self::A) => Some(std::cmp::Ordering::Equal),
        (Self::A, _) => Some(std::cmp::Ordering::Less),
        (_, Self::A) => Some(std::cmp::Ordering::Greater),
        (Self::B, Self::B) => Some(std::cmp::Ordering::Equal),
        (Self::B, Self::C) => Some(std::cmp::Ordering::Less),
        (Self::C, Self::B) => Some(std::cmp::Ordering::Greater),
        (Self::C, Self::C) => Some(std::cmp::Ordering::Equal),
    }
});

// 生成第二个枚举:使用自定义反转排序逻辑
define_ordered_enum!(MyEnum2, {
    match (self, other) {
        (Self::A, Self::A) => Some(std::cmp::Ordering::Equal),
        (Self::A, _) => Some(std::cmp::Ordering::Greater),
        (_, Self::A) => Some(std::cmp::Ordering::Less),
        (Self::B, Self::B) => Some(std::cmp::Ordering::Equal),
        (Self::B, Self::C) => Some(std::cmp::Ordering::Greater),
        (Self::C, Self::B) => Some(std::cmp::Ordering::Less),
        (Self::C, Self::C) => Some(std::cmp::Ordering::Equal),
    }
});

方案对比

  • 组合式方案:更符合Rust的组合优先设计思想,无重复代码,类型安全,枚举值数量多时优势明显;缺点是需要额外的包装类型,使用时需指定排序标记。
  • 宏方案:生成独立枚举,使用方式和普通枚举一致,无需额外包装;缺点是宏的可读性稍弱,修改枚举值时需同步修改宏内部定义。

内容的提问来源于stack exchange,提问作者SPH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:04:54