Rust - создать hashmap, который использует часть данных, которые он хранит от итератора - PullRequest
2 голосов
/ 03 апреля 2020

Я новичок в ржавчине и пытаюсь выяснить, как создать HashMap заимствованных значений из Vec данных, но когда я пытаюсь это сделать, я получаю Vec в HashMap модель собственности борется со мной. Я не знаю, как это сделать, может быть, я просто пробую что-то, что противоречит менталитету Руста.

Например:

struct Data{
    id: String,
    other_value: String,
}

//inside a method somewhere
let data_array = load_data(); // returns a Vec<Data>
let mut hash = HashMap::new(); // HashMap<&String, &Data>

for item in data_array {
    hash.insert(&item.id, &item);
}

Насколько я знаю должен быть способ заполнить эти данные таким образом, поскольку HashMap будет хранить ссылки на исходные данные. Или, может быть, я просто неправильно понял документы ... ¯_ (ツ) _ / ¯

Ответы [ 3 ]

4 голосов
/ 03 апреля 2020

Здесь ключевая проблема заключается в том, что вы потребляете Vec. Циклы for в Rust работают над вещами, которые реализуют IntoIter. IntoIter перемещает Ve c в итератор - сам Ve c больше не существует, как только это будет сделано.

Поэтому элементы, которые вы зацикливаете, исчезают в конце каждой итерации., поэтому эти ссылки в конечном итоге ссылаются на несуществующие данные (висячие ссылки). Если бы вы попытались их использовать, случились бы плохие вещи. Ржавчина мешает вам выстрелить себе в ногу таким образом, поэтому вы получаете сообщение о том, что ссылка не живет достаточно долго. Решение для компиляции вашего кода очень простое. Просто добавьте .iter() в конец l oop, который будет перебирать ссылки, а не потреблять Vec.

for item in data_array.iter() {
    hash.insert(&item.id, item); //Note we don't need an `&` in front of item
}
0 голосов
/ 03 апреля 2020

Я все еще относительно новичок в Rust, так что это может быть не правильно, но я думаю он делает то, что вы хотите, но раз и навсегда - то есть функцию, которая позволяет легко поворачивать коллекция в карту с использованием замыкания для генерации ключей:

fn map_by<I,K,V>(iterable: I, f: impl Fn(&V) -> K) -> HashMap<K,V>
where I: IntoIterator<Item = V>,
      K: Eq + Hash
{
    iterable.into_iter().map(|v| (f(&v), v)).collect()
}

Позволяет вам сказать

 map_by(data_array.iter(), |item| &item.id)

Вот оно на детской площадке:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=87c0e4d1e68ccb6dd3f2c43ac9f318c7

Пожалуйста, подтолкните меня в правильном направлении, если я ошибаюсь.

Есть ли такая функция, лежащая в std?

0 голосов
/ 03 апреля 2020

Получается, что вы можете заимствовать значение итератора, заимствуя коллекцию (Vec). Таким образом, приведенный выше пример превращается в:

for item in &data_array {
    hash.insert(&item.id, item);
}

Обратите внимание на &data_array, который превращает item с типа Data в &Data и позволяет использовать заимствованное значение.

...