使用泛型取消引用强制

Deref coercion with generics

我正在尝试编写一个参数化函数 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 强制转换。有没有办法让这样的东西工作?

HashMap's methods, i.e. get, contains_key, get_mut and remove, can receive a borrowed version of the key type. They do this by using the Borrow trait. They are generic over a type parameter Q that can be any type that can represent a borrow of a key. It works this way: when X implements Borrow<Y>, it means that an &X can be borrowed as a &Y. For example, 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));
}