Search code examples
rustmutability

Mutability issue with self and collections in Rust


I am writing a simple library in Rust that manages a deck of cards. It has functions to shuffle, deal cards etc.

The shuffle() function takes a mutable reference to self that allows the existing deck to be reshuffled. It should be pretty straightforward:

  1. Create a temporary collection from the deck containing a tuple with a card and a random number.
  2. Sort the collection by the random number.
  3. Rebuild the existing deck using the order of the temporary collection.

The code for this would be as follows.

pub struct Deck {
    // A deck contains zero or more cards
    cards: Vec<Card>
}

impl Deck {

// shuffle
pub fn shuffle(&mut self) {
    if self.cards.is_empty() {
        return;
    }

    let mut shuffler : Vec<(&Card, u32)> = Vec::with_capacity(self.cards.len());

    for card in self.cards.iter() {
        // make a tuple consisting of each card in the input and a random number
        let card_pos = (card, rand::thread_rng().gen::<u32>());
        shuffler.push(card_pos);
    }

    // Sort the vector
    shuffler.sort_by_key(|k| k.1);

    // Clear the cards
    self.cards.clear();

    // Put the cards into the new randomized order
    for card_pos in shuffler {
        let (card, _) = card_pos;
        self.cards.push(*card)
    }
}

}

The issue I have is this won't compile because I get errors.

src\deck.rs:85:9: 85:19 error: cannot borrow `self.cards` as mutable because it is also borrowed as immutable [E0502]                                                                                           
src\deck.rs:85         self.cards.clear();                                                              
                       ^~~~~~~~~~                                                                       
src\deck.rs:75:15: 75:25 note: previous borrow of `self.cards` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.cards` until the borrow ends                             
src\deck.rs:75          for card in self.cards.iter() {                                                 
                                    ^~~~~~~~~~                                                          
src\deck.rs:92:6: 92:6 note: previous borrow ends here                                                  
src\deck.rs:68     pub fn shuffle(&mut self) {                                                          
...                                                                                                     
src\deck.rs:92     }                                                                                    
                   ^                                                                                    
src\deck.rs:90:13: 90:23 error: cannot borrow `self.cards` as mutable because it is also borrowed as immutable [E0502]                                                                                          
src\deck.rs:90             self.cards.push(*card)                                                       
                           ^~~~~~~~~~                                                                   
src\deck.rs:75:15: 75:25 note: previous borrow of `self.cards` occurs here; the immutable borrow prevent
s subsequent moves or mutable borrows of `self.cards` until the borrow ends                             
src\deck.rs:75          for card in self.cards.iter() {                                                 
                                    ^~~~~~~~~~                                                          
src\deck.rs:92:6: 92:6 note: previous borrow ends here                                                  
src\deck.rs:68     pub fn shuffle(&mut self) {                                                          
...                                                                                                     
src\deck.rs:92     }                                                                            

The errors complain of mutability which I assume to mean that it doesn't like that I've more than one mutable reference in scope or something but I don't know how to fix it. I've tried using brace delimiters to make blocks for each action but to no avail. I could probably break this up into more than one function but I'd rather it was a single function. How do I make this work with the least amount of effort?

NB I haven't tested the sort functionality yet so I hope sort_by_key does what I think but that's only going to matter after I resolve this first issue.


Solution

  • shuffler is of type Vec<(&Card, u32)>, i.e. the card is a reference. That is, it's a pointer at the Card object stored in the buffer underlying the self.cards vector. So self.cards.clear() would delete the memory under shuffler's feet!

    Luckily there is an easy fix: Don't take references and clear the vector, move the cards out of self.cards with drain:

    let mut shuffler: Vec<(Card, u32)> = Vec::with_capacity(self.cards.len());
    for card in self.cards.drain(..) {
        let card_pos = (card, rand::thread_rng().gen::<u32>());
        shuffler.push(card_pos);
    }
    shuffler.sort_by_key(|k| k.1);
    for card_pos in shuffler {
        let (card, _) = card_pos;
        self.cards.push(card);
    }
    

    Aside: There is an in-place shuffling algorithm that is also more efficient than sorting—linear time instead of O(n log n), and better constant factors—the Fisher-Yates shuffle.