2016-05-30 38 views
9

Sto cercando di capire come funzionano HashMaps in Rust e ho creato questo esempio.Dereferenziazione di stringhe e HashMaps in 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 
} 

Quando provo a compilare il codice con ultima riga non commentata, ottengo il seguente errore

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 

La sezione implementazione volto della docs dà la dereferenziazione come fn deref(&self) -> &str

Allora, qual è sta succedendo qui?

+0

Penso che sia sbagliato (di chiunque abbia scritto 'HashMap :: get') utilizzare il tratto 'Prendi in prestito' qui. Fondamentalmente il legame generico dice: puoi passare un riferimento a qualsiasi tipo a 'get', se il tipo-chiave è preso in prestito come quel tipo. In realtà dovrebbe essere: puoi passare qualsiasi tipo a 'get', purché quel tipo sia coercibile al tipo di chiave. Ma non possiamo sistemarlo all'indietro compatibilmente :( –

risposta

7

L'errore è causato dalla funzione generica HashMap::get su String selezionata dal compilatore durante l'inferenza di tipo. Ma vuoi HashMap::get su str.

Quindi, basta cambiare

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

a

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

per renderlo esplicito. Questo aiuta il compilatore a selezionare la funzione giusta.

Check out Playground here.

Sembra a me che la coercizione Deref<Target> può avvenire solo quando sappiamo che il tipo di destinazione, in modo che quando il compilatore sta cercando di dedurre che HashMap::get da usare, vede &r0.to_string() come tipo &String ma mai &str. E &'static str non implementa Borrow<String>. Ciò provoca un errore di tipo. Quando specifichiamo HashMap::get::<str>, questa funzione si aspetta &str, quando la coercizione può essere applicata a &String per ottenere una corrispondenza &str.

È possibile controllare Deref coercion e String Deref per ulteriori dettagli.

+0

Grazie. Ha senso, tuttavia, non significherebbe che Dref ha due implementazioni? Uno che restituisce & str e altro & String? Il documento non dice nulla su & String. manca qualcosa qui? – skanur

+0

['String' implementa solo' Deref '] (https://doc.rust-lang.org/nightly/std/string/struct.String.html#deref). Questa implementazione rende possibile coerce '& String' in' & str' dove '$ str' è previsto.Vedi [' Deref' coercion] (https://doc.rust-lang.org/nightly/book/deref-coercions.html). – WiSaGaN

1

La definizione del metodo get appare come segue

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

La prima parte è il tipo di oggetto che si passa: Q. Esistono vincoli su Q. Le condizioni sono che Q

  1. il-tipo di chiave K deve applicare la Borrow tratto sopra Q
  2. Q necessità di implementare i tratti Hash e Eq.

Sostituire questo con i tipi attuali significa che il tipo di chiave &'static str deve implementare Borrow<String>. Con la definizione di Borrow, ciò significa che un &'static str deve essere convertibile in &String. Ma tutti i documenti/testi che ho letto affermano che dovresti usare &String dovresti usare lo &str.Quindi ha poco senso offrire una conversione &str ->&String, anche se a volte renderebbe la vita un po 'più semplice.

Dal momento che ogni reference type is borrowable as a shorter lived reference type.), È possibile passare una &str quando un &'static str è il tipo di chiave, perché &'static str implementa Borrow<str>

+0

Grazie La spiegazione su Borrow ha senso! – skanur

5

Le altre risposte sono corrette, ma ho voluto sottolineare che si dispone di un non necessario to_string (hai già collect ed in una String) e un modo alternativo di costringere ad un &str, utilizzando as:

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

In questo caso, sarei probabilmente solo riscrivere la mappa per contenere char come la chiave però:

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

Nota non c'è bisogno di avere un tipo esplicito per la mappa, sarà dedotto.

+0

E solo per aggiungere un'altra alternativa equivalente: 'println! (" {:?} ", roman2number.get (r0.as_str()));', il new-ish [': : as_str'] (https://doc.rust-lang.org/collections/string/struct.String.html#method.as_str) metodo. – c0g