Введение
Вектор организует значения по числовым позициям. HashMap<K, V>, напротив, связывает каждый уникальный ключ типа K с одним значением типа V. Поэтому он удобен для хранения счётчиков, настроек и других данных с ключами.
Вы создадите небольшую карту для подсчёта слов, получите и замените счётчик, воспользуетесь API entry для существующих и отсутствующих слов, а затем переберёте все значения, не полагаясь на неопределённый порядок ключей в карте.
Вставка и получение значений по ключу
На этом шаге вы добавите две пары «ключ–значение» и безопасно получите один счётчик.
Перейдите в каталог проекта и откройте исходный файл:
cd /home/labex/project/word-counts
nano src/main.rs
Подготовленный путь std::collections::HashMap указывает на HashMap внутри модуля коллекций стандартной библиотеки Rust. Строка use присваивает этому длинному пути короткое локальное имя HashMap. Пока воспринимайте это как заранее подготовленный импорт; пути и импорты подробно рассматриваются в следующих модулях Lab. HashMap<String, u32> хранит принадлежащие карте строковые ключи и счётчики беззнакового целого типа. Замените комментарий Step 1a следующим кодом:
let _ = counts.insert(String::from("rust"), 1);
let _ = counts.insert(String::from("safe"), 1);
Метод insert возвращает значение, которое ранее хранилось по этому ключу. Эти ключи новые, поэтому результатом будет None. Конструкция let _ = ...; выполняет операцию и намеренно отбрасывает её результат. Отдельный символ _ отличается от имени вроде _returned_id: шаблон _ не создаёт привязку, которую можно использовать позже.
Замените комментарий Step 1b следующим кодом:
match counts.get("rust") {
Some(count) => println!("Rust count: {count}"),
None => println!("Rust count: missing"),
}
Метод get принимает заимствованный текст "rust" и возвращает Option<&u32>, не перемещая ключ или счётчик из карты. Сохраните файл и выйдите из редактора, затем проверьте и запустите программу:
cargo check
cargo run --quiet
Unique words: 2
Rust count: 1
Первая строка подтверждает, что ключи уникальны. Вторая показывает, что поиск нашёл сохранённый счётчик.
Замена существующего счётчика
На этом шаге вы вызовете insert для уже существующего ключа и проверите значение, которое было заменено.
Откройте исходный файл:
nano src/main.rs
Замените комментарий Step 2 следующим кодом:
let previous = counts.insert(String::from("rust"), 2);
println!("Previous rust count: {previous:?}");
Карта хранит только одно значение для каждого ключа. Новое значение 2 заменяет 1, а возвращённое Some(1) позволяет увидеть, что именно было заменено. Сохраните файл и выйдите из редактора, затем запустите программу:
cargo run --quiet
Новая строка должна выглядеть так:
Previous rust count: Some(1)
Количество уникальных слов остаётся равным двум, поскольку замена не создаёт дополнительный ключ.
Обновление счётчиков с помощью API entry
На этом шаге вы увеличите счётчик одного существующего слова и одного отсутствующего слова с помощью одного и того же API.
entry(key).or_insert(default) возвращает изменяемую ссылку на существующее значение ключа либо вставляет значение по умолчанию и возвращает изменяемую ссылку на него. Откройте исходный файл:
nano src/main.rs
Замените комментарий Step 3 следующим кодом:
*counts.entry(String::from("safe")).or_insert(0) += 1;
*counts.entry(String::from("fast")).or_insert(0) += 1;
println!("Safe count: {}", counts["safe"]);
println!("Fast count: {}", counts["fast"]);
Разберите компактное обновление изнутри наружу. Сначала entry(key) выбирает место, связанное с ключом, независимо от того, занято оно или свободно. Затем or_insert(0) гарантирует наличие значения и возвращает изменяемую ссылку &mut u32 на него.
Начальный * обращается к значению u32, находящемуся за этой ссылкой. Наконец, += 1 добавляет единицу и сохраняет новый счётчик обратно в карту.
Поэтому значение safe становится равным 2, а отсутствующее слово fast сначала получает ноль, а затем увеличивается до 1. Квадратные скобки позволяют прочитать значение по ключу. Индексация карты вызвала бы панику, если бы ключ отсутствовал; здесь она безопасна только потому, что два предыдущих вызова entry гарантируют наличие обоих ключей. Если ключ может отсутствовать, продолжайте использовать get.
Сохраните файл и выйдите из редактора, затем запустите программу:
cargo run --quiet
Теперь последние строки должны выглядеть так:
Safe count: 2
Fast count: 1
Одна операция обработала оба случая: занятый и свободный ключ.
Перебор без зависимости от порядка ключей
На этом шаге вы переберёте все сохранённые счётчики и вычислите сумму, не зависящую от порядка элементов.
Хеш-таблицы не гарантируют стабильный порядок перебора ключей, поэтому при проверке для начинающих не следует ожидать, что ключи будут выведены в определённой последовательности. Метод values() посещает каждое сохранённое значение, чего достаточно для вычисления общей суммы.
Откройте исходный файл и замените комментарий Step 4:
nano src/main.rs
let mut total = 0;
for count in counts.values() {
total += count;
}
println!("Total occurrences: {total}");
Здесь count — это общая ссылка, а += может добавить целое число, на которое она указывает. Сохраните файл и выйдите из редактора, затем проверьте и запустите программу:
cargo check
cargo run --quiet
Последняя строка должна выглядеть так:
Total occurrences: 5
Сумма объединяет значения rust = 2, safe = 2 и fast = 1, не завися от порядка их перебора.
Итоги
Вы добавляли, получали, заменяли и увеличивали счётчики по ключам, а затем перебрали все значения карты, не предполагая стабильного порядка ключей.


