构建锈病中的异源类型列表

时间:2016-10-24 13:28:09

标签: types rust

我对HList的定义如下:

pub trait Data: Any + Debug {}
impl<T: Any + Debug> Data for T {}

/// The empty `HList`.
pub struct Nil;

/// An `HList` with `H` at position 0, and `T` as the rest of the list.
pub struct Cons<H, T> {
    head: PhantomData<H>,
    tail: PhantomData<T>,
}

/// A marker trait that `Nil` and `Cons<H, T>` satisfies.
pub trait HList {}
impl HList for Nil {}
impl<H, T: HList> HList for Cons<H, T> {}

如何通过将类型附加到结尾来构建类型?

将它们作为第一个元素插入是微不足道的:

trait Prepend<D: Data>: Sized {
    fn prepend(self, item: D) -> Cons<D, Self>;
}

impl<D: Data> Prepend<D> for Nil {
    fn prepend(self, item: D) -> Cons<D, Nil> {
        Cons {
            head: PhantomData,
            tail: PhantomData,
        }
    }
}

impl<D: Data, H, T: HList> Prepend<D> for Cons<H, T> {
    fn prepend(self, item: D) -> Cons<D, Cons<H, T>> {
        Cons {
            head: PhantomData,
            tail: PhantomData,
        }
    }
}

Playground link

但最后添加元素,同时保持相同的结构似乎很难。

Nil.prepend(true).prepend(3).prepend("string") 
  -> Cons<&'static str, Cons<i32, Cons<bool, Nil>>>


Nil.push("string").push(3).push(true) 
  -> Cons<&'static str, Cons<i32, Cons<bool, Nil>>>

我知道答案是某种递归函数,它寻找列表中的最后一个Nil并在那里添加当前值,但是我很难为使用这种递归的特征定义一个函数功能

假设我们有一个特性Push,方法push将元素添加到最里面括号中的HList

pub trait Push<?> {
    fn push(self?, el: item) -> ?;
}

如何构建它?

2 个答案:

答案 0 :(得分:8)

使用关联类型的递归似乎可以解决问题:

trait Append<D: Data> {
    type Result;
    fn append(self, item: D) -> Self::Result;
}

impl<D:Data> Append<D> for Nil {
    type Result = Cons<D, Nil>;
    fn append(self, item: D) -> Self::Result {
        Cons {
            head: PhantomData,
            tail: PhantomData,
        }
    }

}

impl<D:Data, H, T:HList+Append<D>> Append<D> for Cons<H,T> {
    type Result = Cons<H, <T as Append<D>>::Result>;
    fn append(self, item: D) -> Self::Result {
        Cons {
            head: PhantomData,
            tail: PhantomData,
        }
    }
}

Playground link

答案 1 :(得分:3)

最好放弃零大小的数据,只是玩类型:

trait Append<D>: Sized {
    type Out;
}

impl<D> Append<D> for Nil {
    type Out = Cons<D, Nil>;
}

impl<D, H, T> Append<D> for Cons<H, T>
    where T: Append<D>
{
    type Out = Cons<H, <T as Append<D>>::Out>;
}

这样你的值可以保持值,如果你想输入数学,你就不要实例化它们。 Here's a full example.