You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中无需手动罗列所有值,获取两个枚举的笛卡尔积?

在Rust中无需手动罗列值生成完整扑克牌组的方法

问题背景

我们用两个枚举分别表示扑克牌的花色和点数:

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

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Rank {
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace,
}

要生成完整的52张牌,本质是求这两个枚举的笛卡尔积,但枚举默认没实现Iterator trait。不想手动罗列所有枚举值,也不想用过程宏,有没有简便方法?另外,这种建模方式是否合理?

解决方案

方法1:借助num_enum crate(轻量属性宏,无需手写大量转换代码)

num_enum可以帮我们给枚举自动绑定数值,并实现FromPrimitive trait,这样就能通过数值范围遍历所有枚举值。

首先在Cargo.toml添加依赖:

[dependencies]
num_enum = "0.7"

修改枚举并实现迭代器:

use num_enum::{IntoPrimitive, FromPrimitive};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, IntoPrimitive, FromPrimitive)]
#[repr(u8)]
enum Suit {
    Spades = 0,
    Hearts = 1,
    Diamonds = 2,
    Clubs = 3,
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, IntoPrimitive, FromPrimitive)]
#[repr(u8)]
enum Rank {
    Two = 0,
    Three = 1,
    Four = 2,
    Five = 3,
    Six = 4,
    Seven = 5,
    Eight = 6,
    Nine = 7,
    Ten = 8,
    Jack = 9,
    Queen = 10,
    King = 11,
    Ace = 12,
}

// 实现Suit的迭代器
struct SuitIter(u8);
impl Iterator for SuitIter {
    type Item = Suit;
    fn next(&mut self) -> Option<Self::Item> {
        let res = Suit::from_primitive(self.0);
        self.0 += 1;
        res
    }
}

impl Suit {
    fn iter() -> SuitIter {
        SuitIter(0)
    }
}

// 实现Rank的迭代器
struct RankIter(u8);
impl Iterator for RankIter {
    type Item = Rank;
    fn next(&mut self) -> Option<Self::Item> {
        let res = Rank::from_primitive(self.0);
        self.0 += 1;
        res
    }
}

impl Rank {
    fn iter() -> RankIter {
        RankIter(0)
    }
}

生成完整牌组:

#[derive(Debug)]
struct Card {
    suit: Suit,
    rank: Rank,
}

fn main() {
    let deck: Vec<Card> = Suit::iter()
        .flat_map(|suit| Rank::iter().map(move |rank| Card { suit, rank }))
        .collect();
    
    println!("{:?}", deck);
}

方法2:纯手动实现(无需第三方库)

如果不想引入外部依赖,可以手动给枚举分配数值并实现TryFrom,通过数值范围遍历:

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
enum Suit {
    Spades = 0,
    Hearts = 1,
    Diamonds = 2,
    Clubs = 3,
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
enum Rank {
    Two = 0,
    Three = 1,
    Four = 2,
    Five = 3,
    Six = 4,
    Seven = 5,
    Eight = 6,
    Nine = 7,
    Ten = 8,
    Jack = 9,
    Queen = 10,
    King = 11,
    Ace = 12,
}

// 实现Suit的TryFrom转换
impl TryFrom<u8> for Suit {
    type Error = ();
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Suit::Spades),
            1 => Ok(Suit::Hearts),
            2 => Ok(Suit::Diamonds),
            3 => Ok(Suit::Clubs),
            _ => Err(()),
        }
    }
}

// 实现Rank的TryFrom转换
impl TryFrom<u8> for Rank {
    type Error = ();
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Rank::Two),
            1 => Ok(Rank::Three),
            2 => Ok(Rank::Four),
            3 => Ok(Rank::Five),
            4 => Ok(Rank::Six),
            5 => Ok(Rank::Seven),
            6 => Ok(Rank::Eight),
            7 => Ok(Rank::Nine),
            8 => Ok(Rank::Ten),
            9 => Ok(Rank::Jack),
            10 => Ok(Rank::Queen),
            11 => Ok(Rank::King),
            12 => Ok(Rank::Ace),
            _ => Err(()),
        }
    }
}

生成牌组:

#[derive(Debug)]
struct Card {
    suit: Suit,
    rank: Rank,
}

fn main() {
    let deck: Vec<Card> = (0..4)
        .filter_map(|val| Suit::try_from(val).ok())
        .flat_map(|suit| {
            (0..13)
                .filter_map(|val| Rank::try_from(val).ok())
                .map(move |rank| Card { suit, rank })
        })
        .collect();
    
    println!("{:?}", deck);
}

建模方式合理性说明

用枚举建模花色和点数完全是正确的选择,这是Rust中处理有限固定值集合的惯用方式。枚举能提供严格的类型安全,避免出现无效的花色或点数(比如用整数可能出现5这种非法花色),比直接用整数表示更可靠、可读性更强。

内容的提问来源于stack exchange,提问作者Jared Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 04:23:17