标准库的 std::collections::HashMap struct 定义如下:

pub struct HashMap<K, V, S = RandomState> {
    base: base::HashMap<K, V, S>,
}

泛型 K 限制 key 的类型, 泛型 V 限制 value 的类型, 当我们需要插入一条数据的时候,可以使用 insert 方法,定义如下:

 pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        self.base.insert(k, v)
 }

很清晰,key/value都分别限制了类型,并需要移动所有权。 当我们需要获取指定 key 的 value 的时候,可以使用 get 方法,定义如下:

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

可以看到,k 的类型并不是我们预想的 K, 而是 &Q,为什么要这么设计呢? 首先我们先看下泛型要求:

  • K 要求实现 Borrow<Q> 特征
  • Q 要求实现 HashEq 特征

我们考虑如下代码:

let mut m: HashMap<String, i32> = HashMap::<String, i32>::new();
m.insert("kobe".to_owned(), 40);

这里我们定义了一个变量 m, 其类型为 HashMap<String, i32>, 这就意味着 K 的类型为 String, V 的类型为 i32, 当我们想要获取 key 为 kobe 的 value 时,我们可以这样:

m.get(&"kobe".to_owned()) // Some(40)

根据 get 方法的签名我们可以知道,此时 QString, KString, 首先, Q 要求实现 HashEq 特征, String 满足; K 要求实现 Borrow<Q> 特征, 也就是说 String 要实现 Borrow<String> 特征, 我们好像在定义 String 的文件里没有找到这个,那它在哪呢?

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Borrow<T> for T {
    #[rustc_diagnostic_item = "noop_method_borrow"]
    fn borrow(&self) -> &T {
        self
    }
}

位于: src/rust/library/core/src/borrow.rs

这个叫做 reflexive implementation(自反实现),他表示:标准库已经为任何类型实现了 Borrow 特征,所以 String 也默认实现了 Borrow<String> 特征. 我们还可以使用如下方式获取 key 为 kobe 的 value:

m.get("kobe") // Some(40)

m.get(&"kobe".to_owned()) 这种方案更加简洁, 为什么可以这样呢? "kobe" 的类型其实是 &str, 这就意味着 Qstr, Q 要求实现 HashEq 特征, str 满足; K 要求实现 Borrow<Q> 特征, 也就是说 String 要实现 Borrow<str> 特征, 我们找找 标准库有没有定义:

#[stable(feature = "rust1", since = "1.0.0")]
impl Borrow<str> for String {
    #[inline]
    fn borrow(&self) -> &str {
        &self[..]
    }
}

确实有,位于: src/rust/library/alloc/src/str.rs 对于上述的例子, get 方法可以接收 &str&String 这样的类型,由于Rust不支持方法重载, 这样可以支持更多类型参数,而且也更加简便.