自有字符串和借用字符串是否保证散列到相同的值?

Ber*_*ard 3 hash hashmap rust

String并且str都实现Hash,所以我们可以散列它们中的任何一个。似乎拥有和借用的字符串当前哈希为相同的值,因此此断言成功:

use std::hash::Hash;
use std::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
pub fn main() {
    let hash1 = {
        let x: String = "abc".to_owned();
        let mut hasher = DefaultHasher::new();
        x.hash(&mut hasher);
        hasher.finish()
    };
    let hash2 = {
        let x: &str = "abc";
        let mut hasher = DefaultHasher::new();
        x.hash(&mut hasher);
        hasher.finish()
    };
    assert!(hash1 == hash2);
}
Run Code Online (Sandbox Code Playgroud)

我正在写关于这种行为的利用代码raw_entry的API HashMap。具体来说,我使用了一个 HashMap,其中键是枚举,但为了减少冗余分配,我想使用这些枚举的“借用”版本进行查找。

换句话说,在下面的代码中,我确实需要保证两个断言都会成功,而不管使用的是什么Hasher实现。在我看来,这将取决于和的Hash实现提供的保证。Stringstr

use std::hash::Hash;
use std::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
pub fn main() {
    {
        #[derive(Hash)]
        enum E1 {
            First(i32),
            Second(String),
        }
        #[derive(Hash)]
        enum E2<'a> {
            First(i32),
            Second(&'a str),
        }
        let hash1 = {
            let x: E1 = E1::First(100);
            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        };
        let hash2 = {
            let x: E2 = E2::First(100);
            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        };
        assert!(hash1 == hash2);
        let hash3 = {
            let x: E1 = E1::Second("abc".to_owned());
            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        };
        let hash4 = {
            let x: E2 = E2::Second("abc");
            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        };
        assert!(hash3 == hash4);
    }
}
Run Code Online (Sandbox Code Playgroud)

是否记录了有关此类保证的任何地方?我认为必须提供此类保证(否则我认为无法正确实现 的contains_key()方法HashMap,因为参数可以是密钥的任何借用形式),但我无法在任何地方找到此保证。

Fra*_*gné 10

是的。这是有保证的,因为String实现了Borrow<str>.

实施合同的一部分Borrow是:

此外,在为其他特性提供实现时,需要考虑它们是否应该与底层类型的那些特性相同,因为它们充当了底层类型的表示。通用代码通常Borrow<T>在依赖于这些附加特征实现的相同行为时使用。这些 trait 可能会作为额外的 trait bound 出现。

特别是EqOrd并且Hash对于借用和拥有的值必须等效:x.borrow() == y.borrow()应该给出与 相同的结果x == y

在标准库中,Borrow特性用于HashMap::get. Borrow可以将 a 传递&strgeta HashMap<String, _>。自然地,为了使其工作,&String并且&str必须为相同的字符串产生相同的散列,因此需要Borrow. 该要求在文档中重复用于HashMap::get

关键可能是地图的主要类型的任何借来的形式,但HashEq借用表格上必须匹配的密钥类型。


Traits 无法在代码中定义这样的要求,因此可能存在不符合要求的实现,因为编译器无法强制执行这些要求。但是,这样的实现会破坏HashMap.