对比Haskell和Rust中的自定义类型

242 阅读1分钟

对比Haskell和Rust中的自定义类型

Haskell中的data VS Rust中的enum

在Haskell中,我们可以通过data关键字定义新类型。最简单的类型声明使用了一个长度有限的值列表。例如,可以这样定义只有5个级别(10、J、Q、K、A)的扑克牌。

-- cards.hs
module Test where
    data Suit = Spades | Hearts | Diamonds | Clubs
    data Rank = Ten | Jack | Queen | King | Ace

在Rust中,可以用enum定义这样的一副扑克牌。

enum Suit {
    Spades,
    Hearts,
    Diamonds,
    Clubs,
}

enum Rank {
    Ten,
    Jack,
    Queen,
    King,
    Ace,
}

当我们试图输出扑克牌的花色(如Hearts)时,无论是Haskell还是Rust,都报错了。

$ ghci        
GHCi, version 9.4.7: https://www.haskell.org/ghc/  :? for help
ghci> :load cards.hs
[1 of 1] Compiling Test             ( cards.hs, interpreted )
Ok, one module loaded.
ghci> Hearts

<interactive>:2:1: error:
    • No instance for (Show Suit) arising from a use of ‘print’
    • In a stmt of an interactive GHCi command: print it
fn main() {
    println!("{} {}", Suit::Hearts, Rank::Ace);
}

// Compiling playground v0.0.1 (/playground)
// error[E0277]: `Suit` doesn't implement `std::fmt::Display`
//   --> src/main.rs:18:23
//    |
// 18 |     println!("{} {}", Suit::Hearts, Rank::Ace);
//    |                       ^^^^^^^^^^^^ `Suit` cannot be formatted with the default formatter
//    |
//    = help: the trait `std::fmt::Display` is not implemented for `Suit`

报错的原因都是尝试显示花色,但却不知道如何显示

若要显示自定义类型,在Haskell中可以继承Show函数,

-- cards.hs
module Test where
    data Suit = Spades | Hearts | Diamonds | Clubs deriving (Show)
    data Rank = Ten | Jack | Queen | King | Ace deriving (Show)

-- ghci> Hearts
-- Hearts
-- ghci> :set +t
-- ghci> Jack
-- Jack
-- it :: Rank
-- ghci> Clubs
-- Clubs
-- it :: Suit

在Rust中可以通过#[derive(Debug)]使用Debug这个trait,并配合:?#:?这样的 format specifier;

#![allow(dead_code)]

#[derive(Debug)]
enum Suit {
    Spades,
    Hearts,
    Diamonds,
    Clubs,
}

#[derive(Debug)]
enum Rank {
    Ten,
    Jack,
    Queen,
    King,
    Ace,
}

fn main() {
    println!("{:?} {:?}", Suit::Hearts, Rank::Ace);
}

// Hearts Ace

也可以实现Display这个trait,

// https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5447530da96d95ce0f76fd6ee7dd3a4d

#![allow(dead_code)]

#[derive(Debug)]
enum Suit {
    Spades,
    Hearts,
    Diamonds,
    Clubs,
}

use std::fmt;
impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Suit::Spades => write!(f, "♠️"),
            Suit::Hearts => write!(f, "♥️"),
            Suit::Diamonds => write!(f, "♦️"),
            Suit::Clubs => write!(f, "♣️"),
        }
    }
}

#[derive(Debug)]
enum Rank {
    Ten,
    Jack,
    Queen,
    King,
    Ace,
}

fn main() {
    println!("{} {:#?}", Suit::Hearts, Rank::Ace);
}
// ♥️ Ace

安装Haskell

$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

参考

《七周七语言》(Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages)8.4.1 类与类型

对比Haskell和Rust中的自定义类型-img01.png