Разыменование строк и HashMaps в Rust

Я пытаюсь понять, как работают HashMaps в Rust, и я придумал этот пример.

use std::collections::HashMap;

fn main() {
    let mut roman2number: HashMap<&'static str, i32> = HashMap::new();
    roman2number.insert("X", 10);
    roman2number.insert("I", 1);

    let roman_num = "XXI".to_string();
    let r0 = roman_num.chars().take(1).collect::<String>();
    let r1: &str = &r0.to_string();
    println!("{:?}", roman2number.get(r1)); // This works

    // println!("{:?}", roman2number.get(&r0.to_string())); // This doesn't
}

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

error: the trait bound `&str: std::borrow::Borrow<std::string::String>` is not satisfied [E0277]
println!("{:?}", roman2number.get(&r0.to_string()));
                                            ^~~
note: in this expansion of format_args!
note: in this expansion of print! (defined in <std macros>)
note: in this expansion of println! (defined in <std macros>)
help: run `rustc --explain E0277` to see a detailed explanation

Раздел реализации черты документов дает разыменование как fn deref(&self) -> &str

Так что здесь происходит?

3 ответа

Решение

Ошибка вызвана этой универсальной функцией HashMap::get над String выбирается компилятором при выводе типа. Но ты хочешь HashMap::get над str,

Так что просто поменяй

println!("{:?}", roman2number.get(&r0.to_string()));

в

println!("{:?}", roman2number.get::<str>(&r0.to_string()));

чтобы сделать это явным. Это помогает компилятору выбрать правильную функцию.

Проверьте игровую площадку здесь.

Мне кажется, что принуждение Deref<Target> может произойти только тогда, когда мы знаем целевой тип, поэтому, когда компилятор пытается определить, какой HashMap::get использовать, он видит &r0.to_string() как тип &String но никогда &str, А также &'static str не реализует Borrow<String>, Это приводит к ошибке типа. Когда мы указываем HashMap::get::<str> эта функция ожидает &str когда принуждение может быть применено к &String чтобы получить соответствие &str,

Вы можете проверить Deref принуждение и String Дереф для более подробной информации.

Другие ответы верны, но я хотел бы отметить, что у вас есть ненужные to_string (ты уже collectв String) и альтернативный способ принуждения к &str, с помощью as:

let r0: String = roman_num.chars().take(1).collect();
println!("{:?}", roman2number.get(&r0 as &str));

В этом случае, я бы просто переписал карту, чтобы она содержала char в качестве ключа, хотя:

use std::collections::HashMap;

fn main() {
    let mut roman2number = HashMap::new();
    roman2number.insert('X', 10);
    roman2number.insert('I', 1);

    let roman_num = "XXI";
    for c in roman_num.chars() {
        println!("{:?}", roman2number.get(&c));
    }
}

Обратите внимание, что нет необходимости иметь явный тип для карты, это будет вывод.

Определение get Метод выглядит следующим образом

fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq

Первая часть - это тип объекта, который вы передаете: Q, Есть ограничения на Q, Условия на Q это что

  1. тип ключа K необходимо реализовать Borrow черта над Q
  2. Q необходимо реализовать Hash а также Eq черты.

Замена этого фактическими типами означает, что тип ключа &'static str необходимо реализовать Borrow<String>, По определению Borrow это означает, что &'static str должен быть конвертируемым в &String, Но все документы / тексты, которые я прочитал, утверждают, что везде, где вы будете использовать &String вы должны использовать &str вместо. Так что нет смысла предлагать &str -> &String обращение, даже если это иногда делает жизнь немного легче.

Поскольку каждый ссылочный тип может быть заимствован как ссылочный тип с более коротким сроком действия.), Вы можете передать &str когда &'static str это тип ключа, потому что &'static str инвентарь Borrow<str>

Другие вопросы по тегам