Возврат изменяемой ссылки в Rust - PullRequest
5 голосов
/ 25 января 2020

Я изучаю Rust, поэтому извиняюсь, если это тривиальный вопрос. Я гуглил в течение часа безрезультатно.

У меня есть массив значений enum. Я sh найду случайное местоположение в этом массиве, которое соответствует указанному шаблону c и вернет изменяемую ссылку на него с намерением изменить элемент в этом месте.

enum Tile {
    Empty,
    ...  // Other enum values
}

fn random_empty_tile(arr: &mut [Tile]) -> &mut Tile {
    loop {
        let i = rand::thread_rng().gen_range(0, arr.len());
        let tile = &mut arr[i];
        if let Tile::Empty = tile {
            return tile;
        }
    }
}

клеточный заем жалуется на две указанные здесь вещи. Первый - это arr.len() звонок. Это запрещено, потому что требуется постоянная ссылка на arr, а у нас уже есть изменяемая ссылка на arr через параметр. Поэтому никакие другие ссылки не могут быть приняты, и вызов не разрешен.

Второй - return tile. Это не удается, поскольку средство проверки заимствования не может доказать, что время жизни этой ссылки совпадает со временем жизни самого arr, поэтому возвращать его небезопасно.

Я думаю, что приведенные выше описания ошибок верны ; Я думаю, что понимаю, что происходит не так. К сожалению, я понятия не имею, как решить любую из этих проблем. Если бы кто-то мог предоставить идиоматическое c решение для достижения такого поведения, было бы очень признательно.

В конечном счете, я бы sh сделал следующее:

let mut arr = [whatever];
let empty_element = random_empty_tile(&mut arr);
*empty_element = Tile::SomeOtherValue;

Таким образом, мутировав массив такой, что заменяется пустое значение.

1 Ответ

5 голосов
/ 25 января 2020

Ответ на задачу

fn random_empty_tile(arr: &mut [Tile]) -> &mut Tile {
    let len = arr.len();
    let mut the_chosen_i = 0;
    loop {
        let i = rand::thread_rng().gen_range(0, len);
        let tile = &mut arr[i];
        if let Tile::Empty = tile {
            the_chosen_i = i;
            break;
        }
    }
    &mut arr[the_chosen_i]
}

будет работать. Вам разрешено использовать изменяемый заем в l oop, только не злоупотребляйте этим, с точки зрения заемщиков. То, что вы фактически делаете, - это многократно повторное заимствование массива. Как всегда, компилятор очень полезен, если вы знаете, как его использовать.

Чтобы добраться до root проблемы, давайте рассмотрим только первые две итерации нашего l oop:

fn random_empty_tile_2<'arr>(arr: &'arr mut [Tile]) -> &'arr mut Tile {
   let len = arr.len();

   // First loop iteration
   {
       let i = thread_rng().gen_range(0, len);
       let tile = &mut arr[i]; // Lifetime: 'arr
       if let Tile::Empty = tile {
           return tile;
       }
   } 

   // Second loop iteration
   {
       let i = thread_rng().gen_range(0, len);
       let tile = &mut arr[i]; // Lifetime: 'arr
        if let Tile::Empty = tile {
           return tile;
       }
   }

   unreachable!();

}

Компилятор говорит нам: заимствование arr, называемое tile, должно иметь то же время жизни, что и сам массив, называемое 'arr, поскольку оно возвращается. В следующей итерации l oop мы снова заимствуем arr для 'arr. Это нарушение правил заимствований.

Некоторые комментарии

Вы не делаете себе одолжение со всей этой изменчивостью. Это может проявиться в жалобе заемщика, которая будет позже приведена в main, когда вы пытаетесь удерживать изменяемую ссылку на значение в arr и одновременно используете arr, как это есть (конечно, если вы подумаете об этом! ) disallowed.

Кроме того, ваш алгоритм выбора случайной пустой плитки опасно спекулятивен. Что если в большом массиве есть только одна пустая плитка? Ваша реализация займет вечность. Сначала рассмотрите возможность фильтрации по всем индексам, указывающим на пустую плитку, затем выберите случайный индекс из этого набора, а затем верните запись, на которую указывает этот индекс. Я не буду предоставлять код для этого, вы получите это самостоятельно:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...