如何使用remove方法扩展基于枚举的多类型容器?

时间:2018-03-26 12:46:12

标签: rust

此问题基于another recent question of mine中提供的信息。我想用Container方法扩展以下remove,该方法将存储数据的所有权返回给调用者。随附的单元测试应解释其所需的行为。

在上一个案例中(参见引用的问题),我会在downcast对象上使用Box<Any>方法,但我不知道如何解决问题而是使用枚举。我很感激指点。

use std::any::{Any, TypeId};
use std::collections::HashMap;

trait GroupTrait {
    fn borrow<T: Any>(&self) -> Option<&T>;
}

struct Container<G> {
    inner: HashMap<TypeId, G>,
}

impl<G> Default for Container<G>
where
    G: GroupTrait,
{
    fn default() -> Self {
        Container {
            inner: Default::default(),
        }
    }
}

impl<G> Container<G>
where
    G: GroupTrait,
{
    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> {
        self.inner.get(&TypeId::of::<T>()).and_then(|g| g.borrow())
    }
    pub fn remove<T: Any>(&mut self) -> Option<T> {
        unimplemented!()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// This should be an user-defined type that implements the Any trait.
    #[derive(Debug, Clone, PartialEq)]
    struct TypeA(u32);

    /// This should be an user-defined type that implements the Any trait.
    #[derive(Debug, Clone, PartialEq)]
    struct TypeB(String);

    /// This is the enum that should replace boxed `Any` trait objects. Users also need to supply
    /// this enum. Maybe they'll need to implement additional traits to get `borrow` to work.
    #[derive(Debug, PartialEq)]
    enum Group {
        A(TypeA),
        B(TypeB),
    }

    impl From<TypeA> for Group {
        fn from(value: TypeA) -> Self {
            Group::A(value)
        }
    }

    impl From<TypeB> for Group {
        fn from(value: TypeB) -> Self {
            Group::B(value)
        }
    }

    impl GroupTrait for Group {
        fn borrow<T: Any>(&self) -> Option<&T> {
            use self::Group::*;
            match *self {
                A(ref i) => Any::downcast_ref(i),
                B(ref i) => Any::downcast_ref(i),
            }
        }
    }

    #[test]
    fn insert() {
        let mut c: Container<Group> = Default::default();
        let data = TypeA(100);
        c.insert(data.clone());
        assert_eq!(
            c.inner.get(&TypeId::of::<TypeA>()),
            Some(&Group::A(data.clone()))
        );
    }

    #[test]
    fn borrow() {
        let mut c: Container<Group> = Default::default();
        let data = TypeA(100);
        c.insert(data.clone());
        let borrowed = c.borrow::<TypeA>();
        assert_eq!(borrowed, Some(&data));
    }

    #[test]
    fn remove() {
        let mut c: Container<Group> = Default::default();
        let data = TypeA(100);
        c.insert(data.clone());
        assert_eq!(c.remove::<TypeA>(), Some(data));
    }
}

1 个答案:

答案 0 :(得分:1)

正如您在评论中提到的,TryFrom是可能的。但是,我会选择Into<Option<T>>

pub fn remove<T: Any>(&mut self) -> Option<T>
where
    G: Into<Option<T>>,
{
    self.inner.remove(&TypeId::of::<T>()).and_then(|g| g.into())
}

Playground

我选择Into<Option<T>>而非TryInto<T>,因为Into<Option<T>>会产生OptionTryInto<T>会产生Result<T, Self::Error>