2016-10-24 18 views
8

Tôi có định nghĩa sau đây của HList:Xây dựng Hetereogenous Loại Lists ở Rust

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> {} 

Làm thế nào tôi có thể xây dựng các loại bằng cách thêm họ cho đến cùng?

Chèn họ là yếu tố đầu tiên là tầm thường:

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

Nhưng phụ thêm các yếu tố trên Cuối cùng, trong khi duy trì cấu trúc tương tự có vẻ khó khăn.

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>>> 

Tôi biết câu trả lời là một số loại hàm đệ quy, mà tìm cách cuối cùng Nil trong danh sách và thêm giá trị hiện có, nhưng tôi gặp khó khăn trong việc xác định một chức năng cho các đặc điểm làm việc với một đệ quy như vậy chức năng.

Giả sử chúng ta có một đặc điểm Push với phương pháp push thêm phần tử vào HList trong khung trong cùng:

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

Làm thế nào người ta sẽ xây dựng nó?

Trả lời

7

Đệ quy vào sử dụng các loại liên quan dường như làm các trick:

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

+0

Đây là những gì tôi muốn. Tuyệt vời. Tôi đã có một giải pháp tương tự, nhưng bằng cách yêu cầu thay đổi đuôi từ 'PhantomData ' thành 'T'. –

3

Đây có thể là tốt hơn để từ bỏ các dữ liệu không có kích thước hoàn toàn và chỉ chơi với các loại:

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>; 
} 

Bằng cách này giá trị của bạn có thể vẫn là giá trị, và nếu bạn muốn làm loại toán học bạn chỉ cần không nhanh chóng chúng. Here's a full example.

Các vấn đề liên quan