如何匹配通用结构字段的具体类型?

时间:2017-03-14 19:08:36

标签: generics rust

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

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>能够例如VarApp应用于App(Var(x), App(...)),即拥有Term

前面提到的结构都是impl Term for Var {} impl<T: Term> Term for Abs<T> {} impl<T: Term> Term for App<T, T> {} s:

App<T, U>

有趣的是我在这里不需要fmt::Display,但希望到目前为止这么好 - 现在我想为前面提到的结构实现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

这两个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

我想根据内容的类型以不同的方式打印def ben_loss(x, x_hat): B0 = tf_median(tf.transpose(x_hat)) F0 = tf.abs(x_hat - B0) + 1e-10 sigma = tf.reduce_mean(tf.sqrt( / 0.4), axis=0) background_term = tf.reduce_mean(F0 / sigma, axis=-1) bce = binary_crossentropy(x, x_hat) loss = bce + background_term return loss 。我试图找到一个相关的问题,但它们主要围绕相关类型。有一个简单的解决方案还是我必须重新定义这些定义?

1 个答案:

答案 0 :(得分:2)

  

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

这很简单,只需为您希望能够打印的每种独特类型的Display实施App

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

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

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!电话;在编译时检查所有内容:

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>

  

但它仍会造成麻烦,例如使用App<Var, App>,因为内部App再次需要2个类型参数

是的,您必须指定泛型,因为App 不是类型,它只是一个踩踏石头。

这一切都取决于你想做什么。最简单的是让App由两种类型组成,只要你不使用它们:

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

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

我猜测后续问题将是针对所有非特殊条件的某种回退或默认情况。类似的东西:

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 ,因此要选择的实现方式不明确。此时,您进入了specialization的领域。这迫使您真正理解孤儿规则

据我了解当前的专业化实现,你不能专注于具体的类型,只能在特征上。