Проблема изменчивости с self и коллекциями в Rust
Я пишу простую библиотеку в Rust, которая управляет колодой карт. Имеет функции перемешивания, раздачи карт и т. Д.
Функция shuffle() принимает изменяемую ссылку на себя, что позволяет перетасовать существующую колоду. Это должно быть довольно просто:
- Создайте временную коллекцию из колоды, содержащей кортеж с картой и случайным числом.
- Сортировать коллекцию по случайному номеру.
- Перестройте существующую колоду, используя порядок временной коллекции.
Код для этого будет следующим.
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)
}
}
}
У меня проблема в том, что это не скомпилируется, потому что я получаю ошибки.
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 }
Ошибки жалуются на изменчивость, которая, как я предполагаю, означает, что ей не нравится, что у меня более одной изменяемой ссылки в области видимости или что-то в этом роде, но я не знаю, как это исправить. Я пытался использовать разделители скобок для создания блоков для каждого действия, но безрезультатно. Я мог бы разбить это на несколько функций, но я бы предпочел, чтобы это была одна функция. Как мне сделать это с наименьшими усилиями?
NB. Я еще не тестировал функцию сортировки, поэтому надеюсь, что sort_by_key сделает то, что я думаю, но это будет иметь значение только после того, как я решу эту первую проблему.
1 ответ
shuffler
имеет тип Vec<(&Card, u32)>
то есть card
это ссылка. То есть это указатель на Card
объект хранится в буфере, лежащем в основе self.cards
вектор. Так self.cards.clear()
удалил бы память под shuffler
ноги!
К счастью, есть простое решение: не берите ссылки и не очищайте вектор, уберите карты из self.cards
с 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);
}
Кроме того: существует алгоритм тасования на месте, который также более эффективен, чем сортировка - линейное время вместо O(n log n) и лучшие постоянные коэффициенты - тасование Фишера-Йейтса.