枚举通用结构 [英] Enumerating generic structs

查看:76
本文介绍了枚举通用结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想尝试使用struct建立Peano数字的正确实现,但是看来我的泛型游戏还不够好,我可以使用一些帮助.我阅读了有关泛型和> 的文档 问题,但它们与我的情况不符.

I wanted to try to build a proper implementation of Peano numbers using structs, but it seems my generics game is not good enough yet and I could use some help. I read the docs on generics and some StackOverflow questions, but they don't fit my case.

我介绍了Peano特征和ZeroSucc类型:

I introduced a Peano trait and Zero and Succ types:

trait Peano {}

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

并为这两种类型实现了Peano特征,以便能够对这两种类型进行抽象:

And implemented a Peano trait for both types to be able to abstract over both:

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

起初我想为Peano实现std::ops::Add,但是很快我发现自己做错了什么,所以我决定从简单的事情开始-枚举:

At first I wanted to implement std::ops::Add for Peano, but I quickly saw that I was doing something very wrong, so I decided to start with something simpler - enumeration:

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>,所以我不确定这是否有帮助.

What am I missing? I experimented with boxing the results (though I would want to avoid that if possible), but the error just changed to mismatched types: Box<Succ<T>> instead of Box<Peano>, so I'm not sure this is helpful.

下面的完整代码:

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

推荐答案

您在Enumerate中有一个T ...没有用.

You have a T in Enumerate... which serves no purpose.

如果回头看一下Peano特性,您会发现它没有T:Succ的实现有一个参数,但是特性本身没有.

If you look back at your Peano trait, you will see that it has no T: the implementation for Succ has a parameter, but the trait itself does not.

这里也一样.

让我们从缩小的范围开始:只能前进的Enumerate.

Let us start with a reduced scope: an Enumerate that can only go forward.

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语法来限制特征的实现者
  • you can refer to the current type as Self, very useful when defining a trait since the type of implementers is unknown in advance
  • you can constrain the implementers of a trait by using the : Peano + Sized syntax after the trait name

现在,您还有一个我未实现的prev方法.事实是,将prev应用于Zero是荒谬的.在这种情况下,我建议您将Enumerate重命名为Next,我将展示如何创建Prev特征:

Now, you also had a prev method which I did not implement. The thing is, it is nonsensical to apply prev to Zero. In this case, I propose that you rename Enumerate to Next and I'll show how to create a Prev trait:

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关联类型,它允许每个实现者指定用于特定情况的类型(并避免使特征的 user ,不得不猜测他们应该使用哪种类型.)

The syntax type Output: Peano + Sized is an associated type, it allows each implementer to specify which type to use for their specific case (and avoid having the user of the trait, having to guess which type they should use).

一旦指定,它就可以在特征中称为Self::Output,也可以从外部称为<X as Prev>::Output(如果X实现了Prev).

Once specified, it can be referred to as Self::Output within the trait or as <X as Prev>::Output from outside (if X implements Prev).

而且由于特征是独立的,因此对于只有 个前辈的Peano个数字,您只有一个Prev实现.

And since the trait is separate, you only have a Prev implementation for Peano numbers that actually have a predecessor.

为什么要使用Sized约束?

目前,Rust要求返回类型的大小已知.这是一个实现上的限制:实际上,调用者必须在堆栈上保留足够的空间,以便被调用者记下返回值.

At the moment, Rust requires that return types have a known size. This is an implementation limit: in practice the caller has to reserve enough space on the stack for the callee to write down the return value.

但是...对于类型级别的计算来说,这是没有用的!那么,我们该怎么办?

However... for type-level computation this is useless! So, what do we do?

好吧,首先,我们添加了一种方便的方法来检查计算结果(比Debug输出更漂亮):

Well, first we add convenient method of checking the result of our computations (prettier than the Debug output):

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

然后...让我们摆脱那些方法,它们什么也没带来;重做的特征因此是:

Then... let's get rid of those methods, they bring nothing; the reworked traits are thus:

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和co,尽管如果使用方法实现特征,则可能需要附加约束.

Now, you can go ahead and implement Add and co, though if you implement traits with methods you might need additional constraints.

这篇关于枚举通用结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆