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

如何避免带泛型参数的Enum将泛型传播至其所在的结构体?

问题描述

我编写了一个包含泛型参数变体的Enum,它被用作应用状态,但该Enum的类型会频繁变化,因此给主结构体添加泛型参数并不合理——我希望应用能适配该Enum的所有变体,需要一种更“通用”的泛型方案。

原代码:

enum WkResponse<T /* 我想根据Resources约束这里的类型 */> {
    User(UserResponse),
    Resource(ResourceResponse<T>),
    Summary(SummaryResponse),
    Pages(PagesResponse),
    Collection(CollectionResponse<T>),
}

pub enum Status {
    None,
    InProgress,
    Done(WkResponse), // 此处编译报错,因为WkResponse需要泛型参数
}

pub struct KaniSabi /* 加<T>的话应用只能支持一种变体 */ {
    current_menu: Menus,
    first_opening: bool,
    api_key: String,
    net_status: Arc<Mutex<Status>>, 
    client: WanikaniClient,           
}

// T可能的所有取值
enum Resources {
    Assignment(Assignment),
    LevelProgression(LevelProgression),
    Reset(Reset),
    Review(Review),
    ReviewStatistic(ReviewStatistic),
    SpacedRepetitionSystem(SpacedRepetitionSystem),
    StudyMaterial(StudyMaterial),
    Subject(Subject),
    VoiceActor(VoiceActor),
}

注:使用了wanikani-rs crate。


解决方案

方案1:用包装枚举替代泛型

直接消除WkResponse的泛型,针对ResourceResponse<T>和CollectionResponse<T>分别创建包装枚举,覆盖所有可能的资源类型:

// 为ResourceResponse创建包装枚举
enum ResourceWrapper {
    Assignment(ResourceResponse<Assignment>),
    LevelProgression(ResourceResponse<LevelProgression>),
    Reset(ResourceResponse<Reset>),
    Review(ResourceResponse<Review>),
    ReviewStatistic(ResourceResponse<ReviewStatistic>),
    SpacedRepetitionSystem(ResourceResponse<SpacedRepetitionSystem>),
    StudyMaterial(ResourceResponse<StudyMaterial>),
    Subject(ResourceResponse<Subject>),
    VoiceActor(ResourceResponse<VoiceActor>),
}

// 为CollectionResponse创建包装枚举
enum CollectionWrapper {
    Assignment(CollectionResponse<Assignment>),
    LevelProgression(CollectionResponse<LevelProgression>),
    Reset(CollectionResponse<Reset>),
    Review(CollectionResponse<Review>),
    ReviewStatistic(CollectionResponse<ReviewStatistic>),
    SpacedRepetitionSystem(CollectionResponse<SpacedRepetitionSystem>),
    StudyMaterial(CollectionResponse<StudyMaterial>),
    Subject(CollectionResponse<Subject>),
    VoiceActor(CollectionResponse<VoiceActor>),
}

// 改造后的WkResponse无需泛型
enum WkResponse {
    User(UserResponse),
    Resource(ResourceWrapper),
    Summary(SummaryResponse),
    Pages(PagesResponse),
    Collection(CollectionWrapper),
}

pub enum Status {
    None,
    InProgress,
    Done(WkResponse), // 编译正常
}

pub struct KaniSabi {
    current_menu: Menus,
    first_opening: bool,
    api_key: String,
    net_status: Arc<Mutex<Status>>, 
    client: WanikaniClient,           
}

优点:完全类型安全,编译器可检查所有分支;缺点:新增资源类型时需要同步更新包装枚举,代码量略大。

方案2:使用Trait Object动态分派

定义通用Trait,让所有资源类型及其包装结构体实现该Trait,通过Trait Object消除泛型:

// 定义通用Trait
pub trait WkResource {}

// 为所有资源类型实现Trait
impl WkResource for Assignment {}
impl WkResource for LevelProgression {}
impl WkResource for Reset {}
impl WkResource for Review {}
impl WkResource for ReviewStatistic {}
impl WkResource for SpacedRepetitionSystem {}
impl WkResource for StudyMaterial {}
impl WkResource for Subject {}
impl WkResource for VoiceActor {}

// 为资源响应结构体实现Trait
impl<T: WkResource> WkResource for ResourceResponse<T> {}
impl<T: WkResource> WkResource for CollectionResponse<T> {}

// 改造后的WkResponse
enum WkResponse {
    User(UserResponse),
    Resource(Box<dyn WkResource>),
    Summary(SummaryResponse),
    Pages(PagesResponse),
    Collection(Box<dyn WkResource>),
}

// 后续Status和KaniSabi的定义无需泛型,直接使用即可

优点:灵活性高,新增资源类型仅需实现Trait;缺点:失去静态类型检查,运行时需处理类型转换,存在微小性能开销。

方案3:拆分WkResponse为具体变体

直接将每种资源对应的响应作为独立变体,完全避免泛型:

enum WkResponse {
    User(UserResponse),
    // ResourceResponse系列变体
    ResourceAssignment(ResourceResponse<Assignment>),
    ResourceLevelProgression(ResourceResponse<LevelProgression>),
    ResourceReset(ResourceResponse<Reset>),
    ResourceReview(ResourceResponse<Review>),
    ResourceReviewStatistic(ResourceResponse<ReviewStatistic>),
    ResourceSpacedRepetitionSystem(ResourceResponse<SpacedRepetitionSystem>),
    ResourceStudyMaterial(ResourceResponse<StudyMaterial>),
    ResourceSubject(ResourceResponse<Subject>),
    ResourceVoiceActor(ResourceResponse<VoiceActor>),
    // CollectionResponse系列变体
    CollectionAssignment(CollectionResponse<Assignment>),
    CollectionLevelProgression(CollectionResponse<LevelProgression>),
    CollectionReset(CollectionResponse<Reset>),
    CollectionReview(CollectionResponse<Review>),
    CollectionReviewStatistic(CollectionResponse<ReviewStatistic>),
    CollectionSpacedRepetitionSystem(CollectionResponse<SpacedRepetitionSystem>),
    CollectionStudyMaterial(CollectionResponse<StudyMaterial>),
    CollectionSubject(CollectionResponse<Subject>),
    CollectionVoiceActor(CollectionResponse<VoiceActor>),
    // 其他非泛型变体
    Summary(SummaryResponse),
    Pages(PagesResponse),
}

优点:类型最明确,匹配分支时逻辑清晰;缺点:变体数量过多,代码显得冗长。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 04:56:32