Как изменить значение в изменяемом HashMap в Rust?
Правильное обновление значения в HashMap через изменяемую ссылку
Ваша ошибка возникает из-за того, что метод get() возвращает неизменяемую ссылку &V, которую нельзя разыменовать для изменения. В Rust для изменения значения нужно получить изменяемую ссылку &mut V. Вот несколько правильных способов:
Способ 1: Использование get_mut()
Самый прямой путь — метод get_mut(), который возвращает Option<&mut V>:
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 100);
scores.insert("Bob", 200);
if let Some(score) = scores.get_mut("Alice") {
*score += 10;
}
println!("{:?}", scores); // {"Alice": 110, "Bob": 200}
}
Способ 2: Использование entry() с and_modify()
Более идиоматичный Rust-подход — использовать API entry(), который обрабатывает и существующие, и отсутствующие ключи:
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 100);
scores.insert("Bob", 200);
scores.entry("Alice")
.and_modify(|score| *score += 10)
.or_insert(50); // выполнится, если ключа нет
println!("{:?}", scores); // {"Alice": 110, "Bob": 200}
}
Способ 3: Прямая вставка с перезаписью
Если вы уверены, что ключ существует, можно просто перезаписать значение через insert():
scores.insert("Alice", scores["Alice"] + 10);
// или
*scores.entry("Alice").or_insert(0) += 10;
Почему ваш код не работал?
scores.get("Alice")заимствуетscoresнеизменяемо, возвращая&i32.- Попытка разыменовать
*scoreкак изменяемое (+=) конфликтует с неизменяемым заимствованием. get_mut()решает эту проблему, предоставляя изменяемую ссылку напрямую.
Рекомендация
Для простого обновления используйте get_mut(). Если нужно одновременно обработать случай отсутствия ключа — entry() с and_modify()/or_insert(). Избегайте insert() с индексированием, так как это может вызвать панику при отсутствии ключа.
Полный пример с обработкой отсутствия ключа
use std::collections::HashMap;
fn update_score(scores: &mut HashMap<&str, i32>, name: &str, delta: i32) {
match scores.get_mut(name) {
Some(score) => *score += delta,
None => println!("Пользователь '{}' не найден", name),
}
}
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 100);
update_score(&mut scores, "Alice", 10);
update_score(&mut scores, "Bob", 10); // Выведет: Пользователь 'Bob' не найден
println!("{:?}", scores); // {"Alice": 110}
}
<<Альтернативный подход: использование HashMap::entry для атомарных операций
Если вы работаете в многопоточной среде или хотите гарантировать атомарность обновления, рассмотрите использование entry() с комбинацией and_modify() и or_insert(). Это предотвращает состояния гонки (race conditions) при параллельном доступе.
use std::collections::HashMap;
fn main() {
let mut scores: HashMap<&str, i32> = HashMap::new();
scores.insert("Alice", 100);
// Атомарное обновление: если ключ есть — увеличить, если нет — вставить 50
scores.entry("Alice")
.and_modify(|e| *e += 10)
.or_insert(50);
// Для ключа "Bob" выполнится or_insert()
scores.entry("Bob")
.and_modify(|e| *e += 10)
.or_insert(50);
println!("{:?}", scores); // {"Alice": 110, "Bob": 50}
}
Этот подход особенно полезен, когда вы не знаете, существует ли ключ, и хотите выполнить разное поведение в обоих случаях. В отличие от get_mut(), entry() не требует предварительной проверки наличия ключа — всё делается одной операцией.
Также обратите внимание на HashMap::retain() для удаления элементов по условию и HashMap::drain() для очистки с извлечением значений.
Ответов пока нет.
Войдите чтобы оставить ответ