Как обновить значение в изменяемом HashMap в Rust?
Основной способ: метод get_mut()
В Rust для обновления значения по ключу в HashMap нужно использовать метод get_mut(), который возвращает Option<&mut V>. Это даёт изменяемую ссылку на значение, если ключ существует.
Вот как исправить ваш пример:
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice".to_string(), 50);
scores.insert("Bob".to_string(), 30);
let key = "Alice".to_string();
if let Some(value) = scores.get_mut(&key) {
*value += 10;
}
println!("{:?}", scores); // {"Alice": 60, "Bob": 30}
}
Обратите внимание на разыменование *value — это необходимо, чтобы получить доступ к самому значению через изменяемую ссылку.
Почему нельзя использовать scores[key]?
В Rust HashMap не реализует трейт IndexMut, потому что это может привести к панике при отсутствии ключа. Вместо этого Rust предлагает явную обработку случая, когда ключа нет — через get_mut() или entry().
Более идиоматичный способ: метод entry()
Если вы хотите обновить значение или вставить новое, если ключа нет, используйте entry():
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice".to_string(), 50);
scores.insert("Bob".to_string(), 30);
let key = "Alice".to_string();
// Если ключ существует — обновляем, если нет — вставляем 0 и добавляем 10
*scores.entry(key.clone()).or_insert(0) += 10;
// Для нового ключа
*scores.entry("Charlie".to_string()).or_insert(0) += 10;
println!("{:?}", scores); // {"Alice": 60, "Bob": 30, "Charlie": 10}
}
Метод entry() возвращает перечисление Entry, которое позволяет выбрать действие:
or_insert(value)— вставляет значение, если ключа нет, и возвращает изменяемую ссылкуand_modify()— вызывает замыкание для изменения существующего значенияor_default()— вставляет значение по умолчанию (если тип реализуетDefault)
Пример с and_modify()
Если нужно только обновить существующее значение, не вставляя новое:
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice".to_string(), 50);
scores.entry("Alice".to_string())
.and_modify(|v| *v += 10)
.or_insert(0); // или просто .or_insert(0) если нужно
println!("{:?}", scores); // {"Alice": 60}
}
Метод and_modify() принимает замыкание, которое получает изменяемую ссылку на значение. Это более декларативный способ, чем ручная проверка get_mut().
Важные моменты
- Заимствование:
get_mut()заимствуетHashMapизменяемо, поэтому пока вы используете полученную ссылку, вы не можете использовать другие ссылки на ту же хеш-таблицу. - Производительность:
entry()выполняет поиск ключа только один раз, в отличие от комбинацииcontains_key()+insert(). - Клонирование ключа: В примере с
entry()я использовалkey.clone(), потому чтоentry()потребляет ключ. Если ключ вам больше не нужен, можно передать его напрямую.
Альтернативный подход: метод insert() с проверкой
Если вы не хотите использовать entry() или get_mut(), можно сначала проверить наличие ключа через contains_key(), а затем обновить через insert():
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice".to_string(), 50);
let key = "Alice".to_string();
if scores.contains_key(&key) {
// Получаем текущее значение и вставляем новое
if let Some(¤t) = scores.get(&key) {
scores.insert(key, current + 10);
}
}
println!("{:?}", scores); // {"Alice": 60}
}
Недостатки этого подхода:
- Поиск ключа выполняется дважды: один раз в
contains_key()и один раз вget(), что менее эффективно. - Код получается более многословным и менее идиоматичным.
- В некоторых случаях может возникнуть конфликт заимствования, если вы попытаетесь использовать
get()иinsert()одновременно.
Этот метод стоит использовать только если вам нужно выполнить сложную логику перед обновлением, которая не помещается в замыкание and_modify(). В остальных случаях предпочтительнее entry() или get_mut().
Ответов пока нет.
Войдите чтобы оставить ответ