Проблема изменчивости с self и коллекциями в Rust

Я пишу простую библиотеку в Rust, которая управляет колодой карт. Имеет функции перемешивания, раздачи карт и т. Д.

Функция shuffle() принимает изменяемую ссылку на себя, что позволяет перетасовать существующую колоду. Это должно быть довольно просто:

  1. Создайте временную коллекцию из колоды, содержащей кортеж с картой и случайным числом.
  2. Сортировать коллекцию по случайному номеру.
  3. Перестройте существующую колоду, используя порядок временной коллекции.

Код для этого будет следующим.

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) и лучшие постоянные коэффициенты - тасование Фишера-Йейтса.

Другие вопросы по тегам