unit-testingrustvectorstructassert

Assert struct vector length


My very first project in rust is a small poker like card game that has five columns players put five cards in. Card is a struct that fetches its value and suite from an external API. Board on the other hand, stores the columns I talked about earlier. Now, I want to test that a Board column doesn't get more than five cards by running a test.

Card:

pub struct Card {
    value: String,
    suit: String,
}

impl Card {
    pub fn new(value: String, suit: String) -> Card {
        Card { value, suit }
    }
}

Board:

use crate::card::Card;

struct Board {
    columns: [Vec<Card>;5]
}

impl Board {
    fn add_card(self){# adds a card}

Test:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn no_card_full_column() {
        let card_one = Card::new(String::from("4"), String::from("Spades"));
        let card_two = Card::new(String::from("3"), String::from("Spades"));
        let card_three = Card::new(String::from("2"), String::from("Spades"));
        let card_four = Card::new(String::from("5"), String::from("Spades"));
        let card_five = Card::new(String::from("6"), String::from("Spades"));
        let mut board = Board {
            columns: [vec![], vec![], vec![], vec![], vec![]],
        };
        board.columns[0].push(card_one);
        board.columns[0].push(card_two);
        board.columns[0].push(card_three);
        board.columns[0].push(card_four);
        board.columns[0].push(card_five);

        board.add_card();
        assert_eq!(&board.columns[0].len(), 5);
    }
}

rust-analyzer complains with can't compare &usize with integer (I'm aware of the usize borrow btw) so I've got a couple of questions:

  1. Do struct vectors allocate memory in a different way that uses usize instead of integer?
  2. I researched docs but didn't get anything I could use so I tried to cast usize to integer, I know that's not elegant but I wanted to pass the test and improve it later (green, red, refactor). I don't feel that casting the length is the answer. Should I still keep tying though?

Solution

  • The compiler is complaining that you are comparing a reference to a value, because you are comparing &_ to _, is like doing &420 == 69 (&i32 to i32).

    So just remove the &

    assert_eq!(board.columns[0].len(), 5);
    

    I suspect the reason you added a & in the first place is because of this error message.

    error[E0382]: borrow of moved value: `board`
      --> ***\src/main.rs:36:16
       |
    26 |     let mut board = Board {
       |         --------- move occurs because `board` has type `Board`, which does not implement the `Copy` trait
    ...
    35 |     board.add_card();
       |           ---------- `board` moved due to this method call
    36 |     assert_eq!(board.columns[0].len(), 5);
       |                ^^^^^^^^^^^^^^^^ value borrowed here after move
    

    but that's because of the previous board.add_card() line, the function take ownership.

    you can confirm this by commenting out the previous line, and the error will go away,

    Solution

    1 Change to reference

    change the function to take reference only, and &mut if you need.

    impl Board {
        fn add_card(&mut self){todo!()}
    }
    

    2 Return Self

    change the function to take ownership and also return Self

    impl Board {
        fn add_card(self)->Self{todo!()}
    }
    

    usage

    let board = board.add_card();
    
    /// you can chain method like this
    let board = board
            .add_card()
            .add_card()
            .add_card()
            .add_card()
            .add_card()
            .add_card();