16

Rust で HashMaps がどのように機能するかを理解しようとしており、この例を思いつきました。

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

ドキュメントの Trait implementation セクションでは、逆参照を次のように指定しています。fn deref(&self) -> &str

では、ここで何が起こっているのでしょうか?

4

3 に答える 3

23

このエラーは、型の推論中にコンパイラによってジェネリック関数HashMap::getオーバーが選択されることが原因で発生します。しかし、Stringあなたはやりたいです。HashMap::getstr

だから変えるだけ

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

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

それを明示すること。これは、コンパイラが正しい関数を選択するのに役立ちます。

ここでプレイグラウンドをチェックしてください。

強制Deref<Target>は、ターゲットの型がわかっている場合にのみ発生するように見えるため、コンパイラがどちらHashMap::getを使用するかを推測しようとすると&r0.to_string()、 type と見なされます&Stringが、決して&str. および&'static strを実装していませんBorrow<String>。これにより、型エラーが発生します。を指定するHashMap::get::<str>と、この関数は&str、強制を適用し&Stringて一致を取得できる場合を期待します&str

Deref詳細については、 coercionStringDerefを確認してください。

于 2016-05-30T08:49:01.420 に答える
7

他の答えは正しいですが、不要なto_string(すでにa にcollect編集済み)と、を使用Stringして a に強制する別の方法があることを指摘したかったのです。&stras

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));
    }
}

マップに明示的な型を指定する必要はなく、推論されることに注意してください。

于 2016-05-30T14:38:43.740 に答える
4

メソッドの定義は次のgetようになります

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

最初の部分は、渡すオブジェクトのタイプです: Q. には制約がありますQ。上の条件Qは、

  1. key-typeは特性Kを実装する必要がありますBorrowQ
  2. QHashおよびEqトレイトを実装する必要があります。

これを実際のタイプに置き換えると、キータイプ&'static strを実装する必要があることを意味しますBorrow<String>。の定義によりBorrow、これは a&'static strが に変換可能である必要があることを意味します&String。しかし、私が読んだすべてのドキュメント/テキストは、使用する場所はどこでも代わりに使用&Stringする必要があると述べています。&strしたがって、&str->&String変換を提供することはほとんど意味がありません。

すべての参照型は短命の参照型として借用できるため、 aがキー型の&str場合に aを渡すことができます。&'static str&'static strBorrow<str>

于 2016-05-30T09:02:56.427 に答える