2016-09-15 82 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)箱子感兴趣。 –