Вставить в hashmap в цикле

Я открываю файл CSV и читаю его, используя BufReader и разбивая каждую строку на вектор. Затем я пытаюсь вставить или обновить счет в HashMap, используя определенный столбец в качестве ключа.

let mut map: HashMap<&str, i32> = HashMap::new();

let reader = BufReader::new(input_file);
for line in reader.lines() {
    let s = line.unwrap().to_string();
    let tokens: Vec<&str> = s.split(&d).collect(); // <-- `s` does not live long enough
    if tokens.len() > c {
        println!("{}", tokens[c]);
        let count = map.entry(tokens[c].to_string()).or_insert(0);
        *count += 1;
    }
}

Компилятор любезно сообщил мне, что s недолговечен. Сохранение внутри цикла заимствованное значение для контейнера во внешней области? предполагает «владение» строкой, поэтому я попытался изменить

let count = map.entry(tokens[c]).or_insert(0);

to

let count = map.entry(tokens[c].to_string()).or_insert(0);

но я получаю ошибку

expected `&str`, found struct `std::string::String`
help: consider borrowing here: `&tokens[c].to_string()`

Когда я добавляю амперсанд (&), возникает ошибка

creates a temporary which is freed while still in use
note: consider using a `let` binding to create a longer lived

В моих знаниях Rust о заимствованиях есть некоторый недостаток. Как я могу заставить хэш-карту владеть строкой, переданной в качестве ключа?


person kometen    schedule 30.03.2020    source источник
comment
Как вы объявили map?   -  person Jmb    schedule 30.03.2020
comment
Спасибо, добавил карту-декларацию. И ты решил мою проблему. Я изменил объявление на HashMap‹String, i32›. Я использовал пример hashmap на rust-lang.org, в котором использовалось &str.   -  person kometen    schedule 30.03.2020
comment
Спасибо, я сделал это, пока мы говорили. Можете ли вы добавить это в качестве ответа?   -  person kometen    schedule 30.03.2020
comment
Трудно ответить на ваш вопрос, потому что он не включает минимально воспроизводимый пример. Мы не можем сказать, какие крейты (и их версии), типы, трейты, поля и т. д. присутствуют в коде. Нам будет легче помочь вам, если вы попытаетесь воспроизвести ошибку на Rust Playground, если возможно, в противном случае в новом проекте Cargo отредактируйте свой вопрос, чтобы включить дополнительную информацию. Существуют советы по MRE для Rust, которые вы можете использовать, чтобы уменьшить исходный код для публикации здесь. Спасибо!   -  person Shepmaster    schedule 30.03.2020
comment
Пожалуйста, отредактируйте свой вопрос и вставьте точную и полную ошибку, которую вы получаете - это поможет нам понять, в чем проблема. так что мы можем помочь лучше всего. Иногда интерпретировать сообщение об ошибке сложно, и на самом деле важна другая часть сообщения об ошибке. Пожалуйста, используйте сообщение от непосредственного запуска компилятора, а не сообщение, созданное IDE, которая может пытаться интерпретировать ошибку для вас.   -  person Shepmaster    schedule 30.03.2020
comment
Спасибо за комментарий. @Jmb указал мне на решение. Ошибка была в том, что я определил хэш-карту с &str. Когда я изменил его на String (т.е. HashMap‹String, i32›), ошибка исчезла.   -  person kometen    schedule 30.03.2020


Ответы (1)


Самый простой способ для этого — чтобы ваша карта владела ключами. Это означает, что вы должны изменить его тип с HasMap<&str, i32> (который заимствует ключи) на HashMap<String, i32>. В этот момент вы можете вызвать to_string, чтобы преобразовать ваши токены в собственные строки:

let mut map: HashMap<String, i32> = HashMap::new();

let reader = BufReader::new(input_file);
for line in reader.lines() {
    let s = line.unwrap().to_string();
    let tokens:Vec<&str> = s.split(&d).collect();
    if tokens.len() > c {
        println!("{}", tokens[c]);
        let count = map.entry(tokens[c].to_string()).or_insert(0);
        *count += 1;
    }
}

Однако обратите внимание, что это означает, что tokens[c] будет дублироваться, даже если он уже присутствует на карте. Вы можете избежать дополнительного дублирования, попытавшись изменить счетчик с помощью get_mut во-первых, но это требует двух поисков, когда ключ отсутствует:

let mut map: HashMap<String, i32> = HashMap::new();

let reader = BufReader::new(input_file);
for line in reader.lines() {
    let s = line.unwrap().to_string();
    let tokens:Vec<&str> = s.split(&d).collect();
    if tokens.len() > c {
        println!("{}", tokens[c]);
        if let Some (count) = map.get_mut (tokens[c]) {
            *count += 1;
        } else {
            map.insert (tokens[c].to_string(), 1);
        }
    }
}

Я не знаю решения, которое копировало бы ключ только тогда, когда не было предыдущей записи, но все равно выполняло бы один поиск.

person Jmb    schedule 30.03.2020
comment
Спасибо, это работает, когда я изменил Key в HashMap‹› на String. Изменил map.entry(), поэтому вариант, который я опубликовал при попытке решить, имел map.entry(t). Позже вернулся к map.entry(tokens[c].to_string()). - person kometen; 30.03.2020