String
and str
both implement Hash
, so we can hash either of them. It appears that both owned and borrowed strings currently hash to the same value, so this assertion succeeds:
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);
}
I'm writing code that leverages on this behaviour in the raw_entry
API of HashMap
. Specifically, I'm using a HashMap where the keys are enums, but to reduce redundant allocations I want to do the lookup using "borrowed" versions of those enums.
In other words, in the following code I do need the guarantee that both assertions will succeed, regardless of the Hasher
implementation being used. It appears to me that this would depend on the guarantees provided by the Hash
implementation of String
and str
.
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);
}
}
Is it documented anywhere about such guarantees? I would suppose that such guarantees must be provided (otherwise I see no way to properly implement the contains_key()
method of HashMap
, since the argument can be any borrowed form of the key), but I can't find this guarantee documented anywhere.
Yes. This is guaranteed because
String
implementsBorrow<str>
.Part of the contract for implementations of
Borrow
is:In the standard library, the
Borrow
trait is used inHashMap::get
.Borrow
makes it possible to pass a&str
toget
on aHashMap<String, _>
. Naturally, in order for this to work,&String
and&str
must produce the same hash for the same string, hence the requirement onBorrow
. The requirement is repeated in the documentation forHashMap::get
:Traits cannot define requirements like this in code, so it is possible for non-conforming implementations to exist as the compiler cannot enforce these requirements. However, such implementations would break
HashMap
.