0

I'm writing code to initialize a deck with 52 cards and shuffle them. In Java, I use an ArrayList and iterate through the Suit enum and the Rank enum, adding a Card(suit,rank) object as I go along. I then use Collections.shuffle().

I am trying to port this code to Rust, using vectors and structs. The problem is that I can't iterate enums like in Java. What is the Rust idiomatic way of trying to achieve this result?

I have tried importing strum & strum_macros to get enum iteration, but I am stuck with trying to push structs onto the Vec and then randomly shuffle it.

Java Code

public class Deck {
    private List < Card > aCards = new ArrayList < > ();

    public Deck() {
        shuffle();
    }
    public void shuffle() {
        aCards.clear();
        for (Suit suit: Suit.values()) {
            for (Rank rank: Rank.values()) {
                aCards.add(new Card(rank, suit));
            }
        }
        Collections.shuffle(aCards);
    }
}

Rust Attempt

use crate::card::Card;
use crate::rank::Rank;
use crate::suit::Suit;
use rand::{thread_rng, Rng};
use strum::IntoEnumIterator;

pub struct Deck {
    cards: Vec<Card>,
}

impl Deck {
    pub fn shuffle(&mut self) -> () {
        self.cards.clear();
        for s in Suit::iter() {
            for r in Rank::iter() {
                self.cards.push(Card::new(s, r));
            }
        }
    }
}

struct for suit (rank is similar)

use strum_macros::*;

#[derive(EnumIter, Debug)]
pub enum Suit {
    SPADES,
    DIAMONDS,
    CLUBS,
    HEARTS,
}

card struct

pub struct Card {
    suit: Suit,
    rank: Rank,
}
impl Card {
    pub fn new(psuit: Suit, prank: Rank) -> Card {
        Card {
            suit: psuit,
            rank: prank,
        }
    }
}

I want to just simply iterate trough two sets of enum variants then shuffle output pairs but this is seemingly much more complicated! I suspect maybe there is a better way?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366

1 Answers1

1

The key points are:

  • bring into scope the required traits for shuffling (SliceRandom for rand version 0.7).
  • bring into scope the required types for the enum::iter()

Cargo.toml:

[package]
name = "mcve"
version = "0.1.0"
authors = ["Svetlin Zarev <svetlin.zarev@xxx.com>"]
edition = "2018"

[dependencies]
strum = "0.15"
strum_macros = "0.15"
rand = "0.7.0"

main.rs:

use strum_macros::EnumIter; // etc.
use strum::IntoEnumIterator;
use rand::thread_rng;
use rand::seq::SliceRandom;

#[derive(Debug, Copy, Clone,EnumIter)]
enum Suit {
    DIAMONDS,
    HEARTS,
    CLUBS,
    SPADES,
}

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

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

impl Card {
    fn new(suit: Suit, rank: Rank) -> Card {
        Card { suit, rank }
    }
}

fn main() {
    let mut cards = Vec::<Card>::new();

    for r in Rank::iter() {
        for s in Suit::iter() {
            cards.push(Card::new(s, r));
        }
    }

    let mut rng = thread_rng();
    cards.shuffle(&mut rng);

    println!("{:?}", cards);
}

As you can see, it's almost as if in Java. The only difference is that some methods do not come from the structs, but from the interfaces (in Rust these are called traits) and you have to import them in order to be able to use them.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82