Попробуйте вставить в HashMap, но ошибка компилятора: невозможно назначить X, потому что он заимствован

Я пытаюсь делать простые вещи с помощью Rust и Excel. В какой-то момент я хочу создать HashMap с Key=Worksheet и Values=Rows. Я использую офисный ящик для работы с Excel.

Теперь я создал следующий код:

let mut workSheets: HashMap<String, Rows> = HashMap::new();
let mut range: Range;

    for conf in config.supa{
        range = excel.worksheet_range(&conf.worksheet).unwrap_or_else(|err|{
            panic!("The Config-File contains worksheets, which are not present in your Excel-File. Notice that names must match exactly. Error: {}", err);
        });

        let _option = workSheets.insert(conf.worksheet, range.rows());
    }

Этот код дает мне следующую ошибку в переменной «диапазон» внутри цикла for:

error[E0506]: cannot assign to `range` because it is borrowed
  --> src/main.rs:45:9
   |
45 |         range = excel.worksheet_range(&conf.worksheet).unwrap_or_else(|err|{
   |         ^^^^^ assignment to borrowed `range` occurs here
...
49 |         let _option = workSheets.insert(conf.worksheet, range.rows());
   |                       -----------------------------------------------
   |                       |                                 |
   |                       |                                 borrow of `range` occurs here
   |                       borrow later used here

For more information about this error, try `rustc --explain E0506`.
error: could not compile `supa_importer` due to previous error

Даже перечитывая это снова и снова, я не понимаю. Как я могу решить это на Rust!?

Я добавил полное сообщение компилятора. До этого был просто вывод ржавчины-анализатора.

Sebi 21.02.2023 11:09

Не используйте ящик office. Это (я бы добавил, бессовестный) ящик с именами, который представляет собой не что иное, как давно устаревшую версию calamine. Так что используйте это, если вам нужна только поддержка чтения; используйте rust_xlsxwriter, если вам нужно написать.

cyqsimon 21.02.2023 11:24
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
2
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Когда вы вызываете range.rows(), он принимает range по ссылке. Смотрите здесь аргумент &self. Невозможно хранить Rows в вашем HashMap, потому что он не владеет базовыми данными (ваша переменная range владеет данными, к которым вы хотите получить доступ через итератор Rows). Лучшим решением было бы хранить Range вместо Rows в вашем HashMap (таким образом запись HashMap будет владеть данными) и вызывать .rows() значение, когда вам это нужно. То есть:

use office::{Excel, Range, Rows};

use std::collections::HashMap;

fn main() {
    let mut workSheets: HashMap<String, Range> = HashMap::new();
    
    for conf in config.supa{
        let range = excel.worksheet_range(&conf.worksheet).unwrap_or_else(|err|{
            panic!("The Config-File contains worksheets, which are not present in your Excel-File. Notice that names must match exactly. Error: {}", err);
        });

        workSheets.insert(conf.worksheet, range).unwrap();
    }
}

Затем вы можете получить доступ к Rows следующим образом, например:

workSheet["foo"].rows()

Во-первых, используйте calamine вместо office. Причина указана в комментарии. Давайте уберем это с дороги в первую очередь.


Перейдем к фактической ошибке: Джонас уже предоставил решение, поэтому я собираюсь объяснить «почему» еще немного и на что обратить внимание.

Ключ в том, чтобы всегда обращать пристальное внимание на то, владеет ли тип всеми своими данными (на жаргоне Rust, «принадлежащий тип») или содержит ли он ссылки на другие данные. Простой способ определить, является ли тип «собственным типом», — это проверить сигнатуру его типа на время жизни (параметр типа, который часто начинается с галочки, часто 'a).

С принадлежащими типами проще всего работать, потому что они содержат (т. е. собственные) собственную копию данных и не связаны (т. е. содержат ссылки) ни с чем другим.

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


Итак, если вы посмотрите на сигнатуру структуры строк (которую вы получаете через функцию Range::rows), вы увидите вышеупомянутую 'a в ее параметрах типа, что имеет смысл, поскольку это итератор данных, принадлежащих структуре Range. . Если вы хотите сохранить эту структуру, вы будете хранить длительную ссылку на структуру Range, на которую она ссылается, что и является вашей ошибкой здесь.

На каждой итерации вашего цикла вы сохраняете Range в переменной range и сохраняете ссылку на базовые данные этого Range (в виде структуры Rows) в HashMap. Но на следующей итерации переменная range переназначается, что означает, что ее старые базовые данные освобождаются, что делает недействительными все ссылки, указывающие на нее. Висячая ссылка является очень распространенной ошибкой в ​​языках, не безопасных для памяти, и не разрешена в безопасном Rust, поэтому ваша ошибка «не может быть назначена range, потому что она заимствована».

Если вам интересно, попробуйте переместить let range = ... внутри цикла и посмотрите, как изменится ошибка. Это может иметь немного больше смысла для вас таким образом.

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