Deref与泛型的强制

div*_*ero 3 generics dereference rust

我正在尝试编写一个参数化函数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
}
Run Code Online (Sandbox Code Playgroud)

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`
Run Code Online (Sandbox Code Playgroud)

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

Fra*_*gné 7

一些HashMap的方法,即get,contains_key,get_mutremove,可以接收键式借来的版本.他们通过使用Borrow特征来做到这一点.它们在类型参数Q上是通用的,可以是任何可以表示借用密钥的类型.它以这种方式工作:当X实现时Borrow<Y>,它意味着&X可以借用一个&Y.例如,StringimplementsBorrow<str>,所以a &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));
}
Run Code Online (Sandbox Code Playgroud)