首页 > 解决方案 > 如何在不知道其类型的情况下获取异构列表的各个元素?

问题描述

给定一个异构列表的实现rust(例如 from frunk),如何在不知道元素具体类型的情况下获得对列表中元素的引用?列表的长度是静态已知的,但不能硬编码为数字文字。

我试图通过按顺序从列表中弹出每个元素(如代码示例所示)并编写一个接受usize索引作为参数的索引方法来获取单个元素。甚至都没有尝试编译。发布的示例是我想做的。

pub trait HList: Sized {
    const LEN: usize;

    type Head;
    type Tail: HList;

    fn push<E>(self, element: E) -> Element<E, Self> {
        Element { head: element, tail: self }
    }

    fn pop(self) -> Option<(Self::Head, Self::Tail)>;

    fn len(&self) -> usize {
        Self::LEN
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct Element<H, T> {
    pub head: H,
    pub tail: T,
}

impl<H, T: HList> HList for Element<H, T> {
    const LEN: usize = 1 + <T as HList>::LEN;

    type Head = H;
    type Tail = T;

    fn pop(self) -> Option<(H, T)> {
        Some((self.head, self.tail))
    }
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct End;

impl HList for End {
    const LEN: usize = 0;

    type Head = End;
    type Tail = End;

    fn pop(self) -> Option<(Self::Head, Self::Tail)> {
        None
    }
}

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

    #[test]
    fn pop_two_for() {
        let h = End
            .push(0usize)
            .push(String::from("Hello, World"));

        fn eval<H: HList>(l: H) {
            for _ in 0usize..H::LEN {
                let (e, l) = l.pop().unwrap();
                // Do work with `e`.
            }
        }

        eval(h);
    }
}

标签: rusttype-level-computation

解决方案


我找到了一个合理的递归解决方案,但只有当我可以HList根据要完成的工作的特征要求修改特征时才有效。这对我的用例来说很好,即使它不像我希望的那样通用。

use std::fmt::Debug;

pub trait MyCustomTrait<'a>: Debug {}

impl<'a> MyCustomTrait<'a> for () {}

impl<'a> MyCustomTrait<'a> for usize {}

impl<'a> MyCustomTrait<'a> for String {}

pub trait HList: Sized {
    const LEN: usize;

    type Head: for<'a> MyCustomTrait<'a>;
    type Tail: HList;

    fn push<E>(self, element: E) -> Element<E, Self>
    where
        E: for<'a> MyCustomTrait<'a>,
    {
        Element::new(element, self)
    }

    fn head(&self) -> &Self::Head;

    fn tail(&self) -> &Self::Tail;

    fn len(&self) -> usize {
        Self::LEN
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct Element<H, T>(H, T);

impl<H, T: HList> Element<H, T> {
    pub fn new(head: H, tail: T) -> Self {
        Element(head, tail)
    }
}

impl<H, T> HList for Element<H, T>
where
    H: for<'a> MyCustomTrait<'a>,
    T: HList,
{
    const LEN: usize = 1 + <T as HList>::LEN;

    type Head = H;
    type Tail = T;

    fn head(&self) -> &Self::Head {
        &self.0
    }

    fn tail(&self) -> &Self::Tail {
        &self.1
    }
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct End;

impl HList for End {
    const LEN: usize = 0;

    type Head = ();
    type Tail = End;

    fn head(&self) -> &Self::Head {
        &()
    }

    fn tail(&self) -> &Self::Tail {
        &End
    }
}

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

    #[test]
    fn artbitrary_recursive() {
        let h = End
            .push(0usize)
            .push(String::from("Hello, World"));

        fn eval<H: HList>(list: &H) {
            if H::LEN > 0 {
                let head = list.head();
                // Do work here, like print the debug representation of `head`.
                eprintln!("{:?}", head);

                eval(list.tail());
            }
        }

        eval(&h);
        assert!(false);
    }
}

推荐阅读