2017-02-14 2 views
3

Ich versuche, eine parametrisierte Funktion if_found_update, die einen Wert in der Hash-Updates zu schreiben, wenn es vorhanden ist:Deref Zwang mit Generika

use std::collections::HashMap; 

fn if_found_update<K, V>(data: &mut HashMap<K, V>, k: &K, v: &V, f: &Fn(&V, &V) -> V) -> bool 
    where K: std::cmp::Eq, 
      K: std::hash::Hash 
{ 
    if let Some(e) = data.get_mut(k) { 
     *e = f(e, v); 
     return true; 
    } 
    false 
} 

fn main() { 
    let mut h: HashMap<String, i64> = HashMap::new(); 
    h.insert("A".to_string(), 0); 
    let one = 1 as i64; 
    fn update(e1: &i64, e2: &i64) -> i64 { 
     e1 + e2 
    }; 
    let k: &str = &"A".to_string(); 
    println!("{}", 
      if_found_update(&mut h, &"A".to_string(), &one, &update)); // works 
    println!("{}", if_found_update(&mut h, k, &one, &update)); // fails to compile 
} 

if_found_update(&mut h, &"A".to_string(), &one, &update); funktioniert gut, aber if_found_update(&mut h, k, &one, &update) irgendwie kompilieren:

error[E0308]: mismatched types 
    --> src/main.rs:24:44 
    | 
24 |  println!("{}", if_found_update(&mut h, k, &one, &update)); // fails to compile 
    |           ^expected struct `std::string::String`, found str 
    | 
    = note: expected type `&std::string::String` 
    = note: found type `&str` 

Ich denke es ist, weil es für die entsprechende deref-Zwang fehlschlägt. Gibt es eine Möglichkeit, so etwas zum Laufen zu bringen?

Antwort

7

Einige HashMap ‚s Methoden, das heißt get, contains_key, get_mut und remove kann eine geliehene Version des Schlüsseltyp erhalten. Sie tun dies mit dem Merkmal Borrow. Sie sind generisch über einen Typparameter Q, der ein beliebiger Typ sein kann, der ein Borgen eines Schlüssels darstellen kann. Es funktioniert so: wenn XBorrow<Y> implementiert, bedeutet dies, dass ein &X als &Y ausgeliehen werden kann. Beispielsweise kann String implements Borrow<str>, also ein als &str ausgeliehen werden.

Sie können dies nutzen, indem Sie einen zusätzlichen Typparameter in Ihre Funktion einfügen und die richtigen Schranken hinzufügen.

use std::borrow::Borrow; 
use std::collections::HashMap; 
use std::hash::Hash; 

fn if_found_update<K, V, Q>(data: &mut HashMap<K, V>, k: &Q, v: &V, f: &Fn(&V, &V) -> V) -> bool 
    where K: Hash + Eq + Borrow<Q>, 
      Q: ?Sized + Hash + Eq 
{ 
    if let Some(e) = data.get_mut(k) { 
     *e = f(e, v); 
     return true; 
    } 
    false 
} 

fn main() { 
    let mut h: HashMap<String, i64> = HashMap::new(); 
    h.insert("A".to_string(), 0); 
    let one = 1 as i64; 
    fn update(e1: &i64, e2: &i64) -> i64 { e1 + e2 } 
    let k: &str = "A"; 
    println!("{}", if_found_update(&mut h, &"A".to_string(), &one, &update)); 
    println!("{}", if_found_update(&mut h, k, &one, &update)); 
} 
+0

Vielen Dank! Ich habe 'Borrow' versucht, aber ich habe es nicht richtig benutzt. – divbyzero