如何避免带泛型参数的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
相关产品推荐
相关产品推荐

