Почему строки из записей csv :: Reader не живут достаточно долго при вставке в HashMap? - PullRequest
0 голосов
/ 18 мая 2018

Я новичок во всей жизни концепции Rust.Я пытаюсь прочитать некоторые данные из файлов CSV и поместить их в HashMap:

extern crate csv;

use std::collections::HashMap;

fn main() {
    let files = vec!["file1.csv", "file2.csv", "file3.csv"];

    let mut topics: HashMap<(&str, &str), &str> = HashMap::new();
    for filename in files {
        let mut rdr = csv::Reader::from_path(filename).unwrap();

        for rec in rdr.records() {
            let rr = rec.unwrap();
            let value1 = rr.get(0).unwrap();
            let value2 = rr.get(1).unwrap();
            topics.insert((filename, value1), value2);
        }
    }
}

Playground link

Однако возникает следующая ошибка:

error[E0597]: `rr` does not live long enough
  --> src/main.rs:14:26
   |
14 |             let value1 = rr.get(0).unwrap();
   |                          ^^ borrowed value does not live long enough
...
17 |         }
   |         - `rr` dropped here while still borrowed
18 |     }
19 | }
   | - borrowed value needs to live until here

Я думал, что вставка в HashMap передает право собственности и, таким образом, записи доступны также вне цикла.Что я тут не так делаю?

Ответы [ 2 ]

0 голосов
/ 19 мая 2018

Проблема в том, что поля CSV живут только до тех пор, пока существует линия CSV, и вы пытаетесь сохранить заимствования полей в теме, которая живет дольше, чем читатель CSV.

Для этого вам нужноHashMap для владения полями CSV путем копирования или перемещения полей в хэш-карту.Для этого вам нужно использовать String s на карте, а не &str s.

Это будет делать то, что вы хотите:

extern crate csv;

use std::collections::HashMap;

fn main() {
    let files = vec!["file1.csv", "file2.csv", "file3.csv"];

    let mut topics: HashMap<(String, String), String> = HashMap::new();
    for filename in files {
        let mut rdr = csv::Reader::from_path(filename).unwrap();

        for rec in rdr.records() {
            let rr = rec.unwrap();
            let value1 = rr.get(0).unwrap();
            let value2 = rr.get(1).unwrap();
            topics.insert((filename.into(), value1.into()), value2.into());
        }
    }
}
0 голосов
/ 19 мая 2018

Посмотрите на этот фрагмент кода:

let mut topics: HashMap<(&str, &str), &str> = HashMap::new();
for filename in files {
    let mut rdr = csv::Reader::from_path(filename).unwrap();

    for rec in rdr.records() {
        let rr = rec.unwrap();
        let value1 = rr.get(0).unwrap();
        let value2 = rr.get(1).unwrap();
        topics.insert((filename, value1), value2);
    }
}

Он создает HashMap, который содержит ссылки на некоторые строки, но кто является владельцем этих строк?Это rr;таким образом, ваше сообщение об ошибке.

После кода:

  1. Reader::from_path читает CSV с диска, и rdr владеет этим результатом.

  2. Документация для Reader::records гласит (выделено мое):

    Возвращает заимствованный итератор сверхвсе записи в виде строк.

    Таким образом, итератор не может пережить API Reader.

  3. StringRecord::get:

    pub fn get(&self, i: usize) -> Option<&str>
    

    Возвращает строковую ссылку, которая живет только до тех пор, пока self.

Путем отслеживания этой строки срезыВы пытаетесь вставить фактические справочные данные, принадлежащие StringRecord.Эти записи удаляются в конце тела цикла for, как показано в сообщении об ошибке.Это может привести к небезопасной памяти, что позволит вам иметь эти ссылки после цикла, поэтому компилятор останавливает вас.

Вставка String s вместо этого позволяет коду продолжить:

topics.insert((filename, value1.to_owned()), value2.to_owned());

Я думал, что вставка в HashMap передает право собственности

Да, это так.Право собственности на ссылки передается.Эти ссылки ссылаются не на

См. Также:

...