Deref胁迫与泛型

时间:2017-02-14 04:45:04

标签: generics rust dereference

我正在尝试编写一个参数化函数if_found_update,如果它存在,则更新散列中的值:

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);工作正常,但if_found_update(&mut h, k, &one, &update)无法编译:

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`

我认为这是因为它没有适当的deref强制。有没有办法让这样的东西工作?

1 个答案:

答案 0 :(得分:7)

某些HashMap方法,即getcontains_keyget_mutremove,可以获得密钥类型的借用版本。他们通过使用Borrow特征来做到这一点。它们在类型参数Q上是通用的,可以是任何可以表示借用密钥的类型。它以这种方式工作:当X实现Borrow<Y>时,这意味着&X可以作为&Y借用。例如,String implements Borrow<str>,因此&String可以作为&str借用。

您可以通过在函数上引入其他类型参数并添加正确的边界来利用此功能。

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