2016-09-15 133 views
0

我想嘗試使用struct來構建適當的Peano數字實現,但似乎我的泛型遊戲還不夠好,我可以使用一些幫助。我閱讀泛型文檔和someStackOverflowquestions,但它們不適合我的情況。枚舉通用結構

我介紹了一個Peano特質和ZeroSucc類型:

trait Peano {} 

struct Zero; 
struct Succ<T: Peano>(T); 

並實施了Peano性狀兩種類型能夠抽象通過兩個:

impl Peano for Zero {} 
impl<T> Peano for Succ<T> where T: Peano {} 

起初我想爲Peano實現std::ops::Add,但我很快發現我做的事情非常錯誤,所以我決定從簡單的事情開始 - 枚舉:

trait Enumerate<T: Peano> { 
    fn succ(&self) -> Succ<T>; 
    fn pred(&self) -> Option<T>; 
} 

impl<T> Enumerate<T> for Zero where T: Peano { 
    fn succ(&self) -> Succ<T> { Succ(*self) } // mismatched types: Zero instead of T 
    fn pred(&self) -> Option<T> { None } 
} 

impl<T> Enumerate<T> for Succ<T> where T: Peano { 
    fn succ(&self) -> Succ<T> { Succ(*self) } // mismatched types: Succ<T> instead of T 
    fn pred(&self) -> Option<T> { Some(self.0) } 
} 

我錯過了什麼?我嘗試了拳擊的結果(儘管如果可能,我想避免這種情況),但錯誤只是更改爲mismatched types: Box<Succ<T>> instead of Box<Peano>,所以我不確定這會有所幫助。

全部下面的代碼:

trait Peano {} 

#[derive(Debug, Clone, Copy, PartialEq)] 
struct Zero; 

#[derive(Debug, Clone, Copy, PartialEq)] 
struct Succ<T: Peano>(T); 

impl Peano for Zero {} 
impl<T> Peano for Succ<T> where T: Peano {} 

trait Enumerate<T: Peano> { 
    fn succ(&self) -> Succ<T>; 
    fn pred(&self) -> Option<T>; 
} 

impl<T> Enumerate<T> for Zero where T: Peano { 
    fn succ(&self) -> Succ<T> { Succ(*self) } 
    fn pred(&self) -> Option<T> { None } 
} 

impl<T> Enumerate<T> for Succ<T> where T: Peano { 
    fn succ(&self) -> Succ<T> { Succ(*self) } 
    fn pred(&self) -> Option<T> { Some(self.0) } 
} 

回答

4

你有一個Enumerate ... T這也是沒有用處的。

如果你回頭看看你的Peano特質,你會看到它有沒有T:爲Succ的實施有一個參數,但性狀本身並沒有。

這裏同樣適用。

讓我們從縮小的範圍開始:只能前進的Enumerate

use std::marker::Sized; 

trait Peano {} 

#[derive(Debug, Clone, Copy, PartialEq)] 
struct Zero; 

#[derive(Debug, Clone, Copy, PartialEq)] 
struct Succ<T: Peano>(T); 

impl Peano for Zero {} 
impl<T> Peano for Succ<T> where T: Peano {} 

trait Enumerate: Peano + Sized { 
    fn succ(self) -> Succ<Self>; 
} 

impl Enumerate for Zero { 
    fn succ(self) -> Succ<Self> { Succ(self) } 
} 

impl<T> Enumerate for Succ<T> where T: Peano { 
    fn succ(self) -> Succ<Succ<T>> { Succ(self) } 
} 

感興趣的幾點:

  • 你可以參考當前類型爲Self,因爲實施者的類型定義特性非常有用的,當是提前未知
  • 可以約束性狀的使用特徵名稱

: Peano + Sized語法實施者

現在,你也有一個prev方法,我沒有實現。問題是,將prev應用於Zero是無意義的。在這種情況下,我建議你重命名EnumerateNext,我將展示如何創建一個Prev特點:

trait Prev: Peano + Sized { 
    type Output: Peano + Sized; 
    fn prev(self) -> Self::Output; 
} 

impl<T> Prev for Succ<T> where T: Peano { 
    type Output = T; 
    fn prev(self) -> Self::Output { self.0 } 
} 

語法type Output: Peano + Sized相關類型,它允許每個實施者指定哪些類型用於他們的特定情況(並且避免使用該特徵的用戶,不得不猜測他們應該使用哪種類型)。一旦指定

,它可以如<X as Prev>::Output從外部(如果X器具Prev)被稱爲性狀內Self::Output或。

而且由於性狀是分開的,你只有一個Prev實施Peano數字實際上前身。


爲什麼Sized約束?

此刻,Rust要求返回類型具有已知大小。這是一個實現限制:實際上,調用者必須在堆棧上預留足夠的空間,以便被調用者記下返回值。

但是...對於類型級別的計算這是沒用的!那麼我們該怎麼辦?

嗯,首先我們添加檢查我們計算的結果(比Debug輸出更漂亮)的簡便方法:

trait Value: Peano { 
    fn value() -> usize; 
} 

impl Value for Zero { 
    fn value() -> usize { 0 } 
} 

impl<T> Value for Succ<T> where T: Value { 
    fn value() -> usize { T::value() + 1 } 
} 

fn main() { 
    println!("{}", Succ::<Zero>::value()); 
} 

然後...讓我們擺脫這些方法,他們帶來什麼;返工特徵是這樣的:

trait Next: Peano { 
    type Next: Peano; 
} 

impl Next for Zero { 
    type Next = Succ<Zero>; 
} 

impl<T> Next for Succ<T> where T: Peano { 
    type Next = Succ<Succ<T>>; 
} 

fn main() { 
    println!("{}", <Zero as Next>::Next::value()); 
} 

和:

trait Prev: Peano { 
    type Prev: Peano; 
} 

impl<T> Prev for Succ<T> where T: Peano { 
    type Prev = T; 
} 

fn main() { 
    println!("{}", <<Zero as Next>::Next as Prev>::Prev::value()); 
} 

現在,你可以繼續執行Add和合作,但如果你有方法實現的特徵,你可能需要額外的限制。

+0

工作就像一個魅力,謝謝! – ljedrz

+0

並且感謝關於類型級計算的額外信息 - 我的特質-foo已經提高了很多。 – ljedrz

+1

@ljedrz:我會注意到,如果你想深入到類型級計算中,你可能會對[typenum](https://crates.io/crates/typenum)箱子感興趣。 –