如何用枚举替代Any特征对象的装箱实现并保留原有API?
To replace Box<Any> with an enum while keeping the original Container API intact, we need to add a trait that lets the enum safely downcast to specific types. Here's a complete, working implementation:
Step 1: Define a Downcasting Trait
First, create a trait that any enum used with Container must implement. This trait handles the core logic of converting enum variants to strongly-typed references:
use std::any::{Any, TypeId}; use std::collections::HashMap; // Trait to enable safe downcasting from enum variants to specific types trait Downcastable { fn downcast_ref<T: Any>(&self) -> Option<&T>; }
Step 2: Update the Container Implementation
Modify the Container to require that G implements our Downcastable trait. This lets the borrow method use the trait's downcasting logic to maintain the original API:
struct Container<G> { inner: HashMap<TypeId, G>, } impl<G> Default for Container<G> { fn default() -> Self { Container { inner: Default::default(), } } } impl<G: Downcastable> Container<G> { pub fn insert<T: Any + Into<G>>(&mut self, data: T) { self.inner.insert(TypeId::of::<T>(), data.into()); } pub fn borrow<T: Any>(&self) -> Option<&T> { // Look up the enum entry by TypeId, then downcast to the desired type self.inner.get(&TypeId::of::<T>()) .and_then(|enum_val| enum_val.downcast_ref::<T>()) } }
Step 3: Implement Traits for Your Custom Enum
Define your enum that wraps all user-defined types, then implement From (to satisfy the Into<G> constraint in insert) and Downcastable:
#[cfg(test)] mod tests { use super::*; /// User-defined type implementing Any #[derive(Debug, Clone, PartialEq)] struct TypeA(u32); /// User-defined type implementing Any #[derive(Debug, Clone, PartialEq)] struct TypeB(String); /// Enum replacing Box<Any> #[derive(Debug, Clone, PartialEq)] enum ContainerEnum { TypeA(TypeA), TypeB(TypeB), } // Allow converting user types directly into the enum impl From<TypeA> for ContainerEnum { fn from(value: TypeA) -> Self { ContainerEnum::TypeA(value) } } impl From<TypeB> for ContainerEnum { fn from(value: TypeB) -> Self { ContainerEnum::TypeB(value) } } // Implement downcasting logic for the enum impl Downcastable for ContainerEnum { fn downcast_ref<T: Any>(&self) -> Option<&T> { match self { // Convert each variant to &dyn Any, then downcast to the target type ContainerEnum::TypeA(a) => (a as &dyn Any).downcast_ref(), ContainerEnum::TypeB(b) => (b as &dyn Any).downcast_ref(), } } } // Verify the API works exactly like the original Container #[test] fn test_container_functionality() { let mut container: Container<ContainerEnum> = Container::default(); // Insert and retrieve TypeA let a = TypeA(42); container.insert(a.clone()); assert_eq!(container.borrow::<TypeA>(), Some(&a)); // Insert and retrieve TypeB let b = TypeB("hello rust".to_string()); container.insert(b.clone()); assert_eq!(container.borrow::<TypeB>(), Some(&b)); // Check that non-existent types return None assert_eq!(container.borrow::<u32>(), None); } }
Key Notes:
- The
Downcastabletrait uses Rust's built-inAnytrait to safely validate and convert enum variants. Each variant is cast to&dyn Anyfirst, thendowncast_refreturns a strongly typed reference only if the type matches. Fromimplementations let users pass custom types directly toinsertwithout manual enum wrapping, keeping the API clean.- The
Containerinterface remains identical to your original implementation—users don't need to change how they interact with it.
内容的提问来源于stack exchange,提问作者E Y

