Как документация гласит:
По умолчанию Hasher
используется RandomState
.Внутренний алгоритм не указан, поэтому на него и его хэши не следует полагаться при переизданиях.
Если мы будем следовать RandomState
...
Конкретный экземпляр RandomState
создаст те же экземпляры Hasher
, но хэши, созданные двумя разными RandomState
экземплярами, вряд ли приведут к одному и тому же результату для одинаковых значений.
Обоснование :
По умолчанию HashMap
использует алгоритм хеширования, выбранный для обеспечения устойчивости к атакам HashDoS.Алгоритм заполняется случайным образом, и предпринимаются разумные усилия для создания этого начального числа из высококачественного безопасного источника случайности, предоставляемого хостом, без блокировки программы.Из-за этого случайность начального числа зависит от качества вывода генератора случайных чисел системы при создании начального числа.В частности, семена, генерируемые при ненормально низком уровне энтропийного пула системы, например при загрузке системы, могут иметь более низкое качество.
Я немного покопался в нем, и нет требования, чтобыhash()
и write()
ведут себя одинаково.
Единственное требование - k1 == k2 -> hash(k1) == hash(k2)
для черты Hash
,Черта Hasher
обладает тем же свойством, но не требуется, чтобы k1 -> hash(k1) == hasher(k1)
.
Это имело смысл, поскольку черта Hash
предназначена для реализации пользователем, и они могут реализовать ее каким нравится.Например, можно добавить salt в хеш.
Вот минимальный полный и не поддающийся проверке пример, который может выдавать либо один и тот же вывод, либо различный вывод, в зависимости от реализации.:
use std::collections::hash_map::{DefaultHasher, RandomState};
use std::hash::{BuildHasher, Hasher, Hash};
fn main() {
let s = RandomState::new();
let mut hasher = s.build_hasher();
b"Cool".hash(&mut hasher);
println!("Hash is {:x}", hasher.finish());
let mut hasher = s.build_hasher();
hasher.write(b"Cool");
println!("Hash is {:x}", hasher.finish());
let s = DefaultHasher::new();
let mut hasher = s.clone();
b"Cool".hash(&mut hasher);
println!("Hash is {:x}", hasher.finish());
let mut hasher = s.clone();
hasher.write(b"Cool");
println!("Hash is {:x}", hasher.finish());
}
Вы можете увидеть , что реализация Hash
для среза также записывает длину среза:
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Hash> Hash for [T] {
fn hash<H: Hasher>(&self, state: &mut H) {
self.len().hash(state);
Hash::hash_slice(self, state)
}
}
Также, похоже, что hash_slice()
имеет то поведение, которое вам нужно, но не указано, что так будет всегда (но я думаю, что это предполагаемое поведение, и оно не изменится, я спросил здесь ).
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
fn main() {
let s = DefaultHasher::new();
let mut hasher = s.clone();
std::hash::Hash::hash_slice(b"Cool", &mut hasher);
println!("Hash is {:x}", hasher.finish());
let mut hasher = s.clone();
hasher.write(b"Cool");
println!("Hash is {:x}", hasher.finish());
}