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:
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.
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.