标准库的 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要求实现Hash和Eq特征
我们考虑如下代码:
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 方法的签名我们可以知道,此时 Q 为 String, K 为 String, 首先, Q 要求实现 Hash 和 Eq 特征, 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, 这就意味着 Q 为 str, Q 要求实现 Hash 和 Eq 特征, 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不支持方法重载, 这样可以支持更多类型参数,而且也更加简便.