如何匹配泛型结构字段的具体类型? [英] How to match on concrete types of generic struct fields?

查看:69
本文介绍了如何匹配泛型结构字段的具体类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究lambda演算,但是由于我不太精通泛型,所以我想确认对某些事情的理解并解决错误.请考虑以下定义:

I'm playing around with the lambda calculus, but since I'm not too generics-savvy, I would like to confirm my understanding of some things and resolve an error. Consider the following definitions:

pub trait Term {} // a lambda term - a variable, an abstraction or an application

pub struct Var(pub usize); // a variable, De Bruijn style
pub struct Abs<T: Term>(T); // an abstraction
pub struct App<T: Term, U: Term>(T, U); // application of T on U

我了解(即无法正常运行),我需要App<T: Term, U: Term>上具有通用性,而不是仅在<T: Term>上才能够例如将Var应用于App,即具有App(Var(x), App(...)).

I understand (i.e. otherwise it doesn't work) that I need App to be generic over <T: Term, U: Term> as opposed to just <T: Term> to be able to e.g. apply a Var to an App, i.e. to have an App(Var(x), App(...)).

上述结构都是Term:

impl Term for Var {}
impl<T: Term> Term for Abs<T> {}
impl<T: Term> Term for App<T, T> {}

有趣的是,在这里我不需要App<T, U>,但希望到目前为止一切都很好-现在,我想为上述结构实现fmt::Display:

Interesting that I don't need App<T, U> here, but hopefully so far so good - now I would like to implement fmt::Display for the aforementioned structs:

use std::fmt;
use std::fmt::Display;

impl fmt::Display for Var {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl<T: Term+Display> Display for Abs<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "λ{}", self.0)
    }
}

这两个impl工作正常;我将它们包括在内,因为下一个依赖它们:

These two impls work just fine; I'm including them since the next one relies on them:

impl<T: Term+Display> Display for App<T, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            App(Var(_), Var(_)) => write!(f, "{}{}", self.0, self.1),
            _ => unimplemented!()
        }
    }
}

但是它失败了:

error[E0308]: mismatched types
  --> src\ast.rs:34:8
   |
34 |                    App(Var(_), Var(_)) => write!(f, "{}{}", self.0, self.1),
   |                        ^^^^^^ expected type parameter, found struct `ast::Var`
   |
   = note: expected type `T`
   = note:    found type `ast::Var`

我想根据其内容类型以不同的方式打印App.我试图找到一个相关的问题,但它们主要围绕关联的类型.是否有一个简单的解决方案,或者我必须重新定义这些定义?

I would like to print App differently based on the types of its contents. I tried to find a related question, but they mostly revolve around associated types. Is there a simple solution or do I have to rework the definitions?

推荐答案

我想根据其内容类型以不同的方式打印App.

这很容易,只需为希望打印的每种App唯一类型实现Display:

That's easy enough, just implement Display for every unique type of App you wish to be able to print:

use std::fmt;

struct App<T, U>(T, U);

impl fmt::Display for App<i32, bool> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Awesome choice!")
    }
}

impl fmt::Display for App<bool, String> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Woah, a string? Really?")
    }
}

fn main() {
    println!("{}", App(42i32, false));
    println!("{}", App(true, "wow".to_string()));
}

您还可以接受具有自己的泛型的其他类型:

You can also accept further types that have their own generics:

impl<T> fmt::Display for App<Vec<T>, char> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} of them; {}", self.0.len(), self.1)
    }
}

请注意,这没有unimplemented!调用;在编译时检查所有内容:

Note that this has no unimplemented! call; everything is checked at compile time:

error[E0277]: the trait bound `App<bool, bool>: std::fmt::Display` is not satisfied
  --> src/main.rs:24:24
   |
24 |         println!("{}", App(true, false));
   |                        ^^^^^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `App<bool, bool>`
   |
   = note: `App<bool, bool>` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
   = note: required by `std::fmt::Display::fmt`

这在标准库中有一个相似的类: Cursor .这种类型接受通用类型,但是所有有趣的功能仅针对少数几个具体类型(例如&[u8]Vec<u8>)实现.

This has a close analog in the standard library: Cursor. This type accepts a generic, but all of the interesting functionality is only implemented for a handful of concrete types like &[u8] or Vec<u8>.

但是它仍然会引起麻烦,例如App<Var, App>,因为内部App再次需要2个类型参数

but it still causes trouble, e.g. with App<Var, App>, because the inner App expects 2 type arguments again

是的,您必须指定泛型,因为App 不是类型,它只是通向一个类型的垫脚石.

Yes, you have to specify the generics because App isn't a type, it's only a stepping stone towards one.

这完全取决于您想做什么.最简单的是,只要不使用两种类型的App,就可以使用它们:

It all depends on what you want to do. The simplest is to just have an App composed of two types of any kind, so long as you don't use them:

impl<T, U> fmt::Display for App<i32, App<T, U>> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Semi-awesome")
    }
}

如果您希望能够显示App,则需要限制泛型,以便显示App:

If you want to be able to display the App, then you need to restrict the generics such that App is displayable:

impl<T, U> fmt::Display for App<i32, App<T, U>>
    where App<T, U>: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}) squared!", self.1)
    }
}

使用

fn main() {
    let a = App(42i32, false);
    println!("{}", a);
    let b = App(100, a);
    println!("{}", b);
    let c = App(100, b);
    println!("{}", c);
}


我猜测后续问题将是关于所有非特殊条件具有某种后备或默认情况的问题.像这样:


I have a guess that the follow up question will be something about having some kind of fallback or default case for all the non-special conditions. Something like:

impl fmt::Display for App<i32, bool> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Awesome choice!")
    }
}

impl<T: fmt::Display, U: fmt::Display> fmt::Display for App<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Boring: {} + {}", self.0, self.1)
    }
}

不,不会编译! i32bool 也实现了Display ,因此选择哪种实现方式模棱两可.此时,您已进入专业化.这迫使您真正了解孤立规则.

Nope, that won't compile! i32 and bool also implement Display so it is ambiguous which implementation to select. At this point, you are into the realm of specialization. This forces you to truly understand the orphan rules.

据我了解,当前专业化的实现,您不能只专注于具体类型,而只能专注于特征.

As far as I understand the current implementation of specialization, you cannot specialize on a concrete type, only on traits.

这篇关于如何匹配泛型结构字段的具体类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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