How can I derive Hash for a struct containing a HashMap? - rust

I'm trying to declare a struct which contains a HashMap, but it won't let me derive Hash.
use std::collections::HashMap;
#[derive(PartialEq,Eq,Hash)]
struct Environment{
names:HashMap<String,String>,
}

HashMap itself does not implement Hash, so an automatic derivation is impossible. You would need to implement Hash manually. Note that you have to be careful to ensure that a manual implementation is compatible with the PartialEq implementation of HashMap; for example, you would need to ensure that you hash key-value pairs in a consistent order, because key order is irrelevant to the PartialEq implementation.
One possible implementation collects all (key, value) pairs into a vector, sorts it on key, and hashes the resulting vector:
impl Hash for Environment {
fn hash<H: Hasher>(&self, h: &mut H) {
let mut pairs: Vec<_> = self.names.iter().collect();
pairs.sort_by_key(|i| i.0);
Hash::hash(&pairs, h);
}
}
Note that this requires allocating a vector of self.len() elements, but does not require duplicating the strings (they are borrowed).
Consider instead using BTreeMap<String, String>. This type implements Hash, so automatic derivation of Hash is possible:
#[derive(PartialEq, Eq, Hash)]
struct Environment{
names: BTreeMap<String, String>,
}

Related

Cannot pass in generic type to a struct initialization

I am trying to initalize a struct H256 and passing in data as the parameter. Here is the trait and struct definition:
/// An object that can be meaningfully hashed.
pub trait Hashable {
/// Hash the object using SHA256.
fn hash(&self) -> H256;
}
/// A SHA256 hash.
#[derive(Eq, PartialEq, Serialize, Deserialize, Clone, Hash, Default, Copy)]
pub struct H256(pub [u8; 32]); // big endian u256
impl Hashable for H256 {
fn hash(&self) -> H256 {
ring::digest::digest(&ring::digest::SHA256, &self.0).into()
}
}
Here is another struct's method where I initalize the struct H256:
impl MerkleTree {
pub fn new<T>(data: &[T]) -> Self
where
T: Hashable,
{
let a = H256(data);
}
...
}
Here is the error :mismatched types expected array [u8; 32]found reference&[T]`
What is the problem?
The answer is simple. When you make the struct H256, the inner must be list of bytes with 32 element. However, when you call H256(data);, data has type &[T], and it is not the list of bytes with 32 element.
+)
It seems that you want to hash the list of T, the type can be hashed. Then what you need to do is,
You must indicate how to hash the object of type T.
You must indicate how to hash the list of hashable type.
You achieve 1, by trait bound Hashable in function MerkleTree::new. So you need to do implement trait for &[T] when T is bounded by trait Hashable.

Storing objects of any type in Rust

I want to implement a class Storage that can store objects of any types. I am trying to do that using trait Any. The Storage::insert::<T>(key, value) should add a pair, where the key is always some String type, and the value can be any type. When I store a Box<HashMap<String, dyn Any>> the compiler says that it doesn't have size at compile-time. So how can I avoid that error ?
use std::any::{Any, TypeId};
use std::collections::hash_map::Keys;
use std::collections::HashMap;
pub struct Storage where Self: Sized{
map: Box<HashMap<String, dyn Any>>,
}
impl Storage {
pub fn new() -> Self {
Self {
map: Some(Box::new(HashMap::new())),
}
}
pub fn insert<Q: Any>(&mut self, key: &dyn Any, obj: Q) {
if key.is::<String>() {
let key_string = key.downcast_ref::<String>().unwrap();
self.map.as_mut().insert(key_string.clone(), obj);
}
}
}
Also I'm not sure that such class can be implemented with std::collections::HashMap
The problem is that HaspMap<K, V> needs to have Sized types K and V, which is not the case of dyn Any. This means that the size of K and V must be known at compile time. For instance, you always know that an u32 will take 32 bits, but the size of [u32] is not known beforehand. Therefore: u32: Sized, and [u32]: !Sized.
This is also the case of dyn Any, dyn Any: !Sized, because objects of different size can implement Any, and, in fact, every object implements Any.
To solve this, there is an easy patch, and it's wrapping that type with Box<_>. Box is a pointer to a heap-allocated memory, so you know its size will always be the size of a pointer (roughly speaking), and you don't need to know at compile time how much memory you will allocate on the heap. Here it goes:
pub struct Storage {
map: Box<HashMap<String, Box<dyn Any>>>,
}
You will also have to adapt the rest of the code, but you can find everything in the documentation.

Making a HashMap in rust with an unboxable trait as a key? [duplicate]

I want to be able to use a variety of different types as keys in a HashMap, all of which would implement Hash. This seems like it should be possible: from reading the docs it seems that every Hasher will produce a u64 result, so they eventually get reduced down to a common type. Effectively I want to do:
use std::{collections::HashMap, hash::Hash};
fn x(_: HashMap<Box<dyn Hash>, ()>) {}
which I'm not allowed to do:
error[E0038]: the trait `std::hash::Hash` cannot be made into an object
--> src/lib.rs:3:9
|
3 | fn x(_: HashMap<Box<dyn Hash>, ()>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::hash::Hash` cannot be made into an object
It seems like I could create a Hasher (e.g. RandomState), use that to manually calculate hash values, then store the u64 result in a HashMap<u64, _> but that seems kind of overly complex. I don't ever want to get the key values back out again, I just need to be able to compare the hash values. Is there a HashMap alternative I'm not aware of? Or am I looking at this in a totally wrong way?
It seems like I could create a Hasher (e.g. RandomState), use that to manually calculate hash values, then store the u64 result in a HashMap<u64, _> but that seems kind of overly complex.
Unfortunately that's overly simple - since a hash function discards some information, hash tables don't just work on hashes, they also need the original key to compare it with the key you're inserting or looking for. This is necessary to handle the case when two keys produce the same hash, which is both possible and allowed.
Having said that, you can implement Python-style hash tables with heterogeneous keys in Rust, but you'll need to box the keys, i.e. use Box<dyn Key> as the type-erased key, with Key being a trait you define for all concrete types you want to use as the hash key. In particular, you'll need to:
define the Key trait that specifies how to compare and hash the actual keys;
(optionally) provide a blanket implementation of that trait for types that are themselves Hash and Eq so the user doesn't need to do that for every type manually;
define Eq and Hash for Box<dyn Key> using the methods the trait provides, making Box<dyn Key> usable as key in std::collections::HashMap.
The Key trait could be defined like this:
trait Key {
fn eq(&self, other: &dyn Key) -> bool;
fn hash(&self) -> u64;
// see https://stackoverflow.com/a/33687996/1600898
fn as_any(&self) -> &dyn Any;
}
And here is a blanket implementation of Key for any type that is Eq and Hash:
use std::any::{Any, TypeId};
use std::collections::{HashMap, hash_map::DefaultHasher};
use std::hash::{Hash, Hasher};
impl<T: Eq + Hash + 'static> Key for T {
fn eq(&self, other: &dyn Key) -> bool {
if let Some(other) = other.as_any().downcast_ref::<T>() {
return self == other;
}
false
}
fn hash(&self) -> u64 {
let mut h = DefaultHasher::new();
// mix the typeid of T into the hash to make distinct types
// provide distinct hashes
Hash::hash(&(TypeId::of::<T>(), self), &mut h);
h.finish()
}
fn as_any(&self) -> &dyn Any {
self
}
}
Finally, these impls will make Box<dyn Key> usable as hash table keys:
impl PartialEq for Box<dyn Key> {
fn eq(&self, other: &Self) -> bool {
Key::eq(self.as_ref(), other.as_ref())
}
}
impl Eq for Box<dyn Key> {}
impl Hash for Box<dyn Key> {
fn hash<H: Hasher>(&self, state: &mut H) {
let key_hash = Key::hash(self.as_ref());
state.write_u64(key_hash);
}
}
// just a convenience function to box the key
fn into_key(key: impl Eq + Hash + 'static) -> Box<dyn Key> {
Box::new(key)
}
With all that in place, you can use the keys almost as you would in a dynamic language:
fn main() {
let mut map = HashMap::new();
map.insert(into_key(1u32), 10);
map.insert(into_key("foo"), 20);
map.insert(into_key("bar".to_string()), 30);
assert_eq!(map.get(&into_key(1u32)), Some(&10));
assert_eq!(map.get(&into_key("foo")), Some(&20));
assert_eq!(map.get(&into_key("bar".to_string())), Some(&30));
}
Playground
Note that this implementation will always consider values of distinct concrete types to have different values. While Python's dictionaries will consider keys 1 and 1.0 to be the same key (while "1" will be distinct), an into_key(1u32) will be distinct not just from into_key(1.0), but also from into_key(1u64). Likewise, into_key("foo") will be a distinct from into_key("foo".to_string()). This could be changed by manually implementing Key for the types you care about, in which case the blanket implementation must be removed.
I could not understand if you need to compare 2 hash or insert a hash into a HashMap, so i'm going to give you aswears to both.
This generic function generate hash for any given type.
fn hash<T>(obj: T) -> u64
where
T: Hash,
{
let mut hasher = DefaultHasher::new();
obj.hash(&mut hasher);
hasher.finish()
}
To compare you would do something like this :
let string = String::from("hello");
let _i32 = 12;
let result = hash(&string) == hash(_i32);
println!("{} == {} -> {}", &string, _i32, result);
This also work for struct :
#[derive(Hash, Debug)]
struct Person{
name : String,
age : i32
}
let person1 = Person{name : "Kelvin".to_string(), age: 19};
let person2 = Person{name : "Dmitri".to_string(), age: 17};
let result = hash(&person1) == hash(&person2);
println!("{:?} == {:?} -> {}", &person1, &person2, result);
To insert Hash into a HashMap :
let mut HashMap : HashMap<u64, ()> = HashMap::new();
HashMap.insert(hash("hello world"), ());

Creating a HashMap with a function with reference paramaters as key

I'm trying to create a HashMap where the key type is a function with a reference paramater.
let mut hm: HashMap<fn(&u32) -> u32, u32> = HashMap::new();. This works fine, but i cant insert anything into the map. The compiler says it's not legal since trait bounds weren't satisfied
rules.insert(
|v: &u32| v + 1,
0,
);
gives
the method `insert` exists but the following trait bounds were not satisfied:
`for<'r> fn(&'r u32) -> u32: Eq`
`for<'r> fn(&'r u32) -> u32: Hash`
I've read about Lifetimes in the various texts, but i can't figure out how to solve this.
Background: I'm implementing wave function collapse. I want my implementation to be generic in the sense that any "grid" can be used, 1d, 2d, 3d, nd, hexagonal etc. To do this i use a "ruleset" which is a hashmap where the key is a function that takes a cell coordinate and returns its neighbours, and the value is the rule for how to collapse said neighbours' states. The key function takes an index and a reference to the "grid" and returns the index of the neihbour.
If you want to support multiple grid implementations, the most natural approach is to define a Grid trait that abstracts differences between the grids. Here's one I made up, loosely based on your use case description:
enum CollapseRule {
A,
B,
}
trait Grid {
const COLLAPSE_RULE: CollapseRule;
fn neighbours(&self, index: usize) -> Vec<usize>;
}
#[derive(Clone, Debug, Default)]
struct Grid1d {
vertices: Vec<f64>,
}
impl Grid for Grid1d {
const COLLAPSE_RULE: CollapseRule = CollapseRule::A;
fn neighbours(&self, index: usize) -> Vec<usize> {
let len = self.vertices.len();
match index {
0 => vec![1],
_ if index < len => vec![index - 1, index + 1],
_ if index == len => vec![index - 1],
_ => panic!(),
}
}
}
Whereever your code needs to accept a grid, you can accept a generic type G with a trait bound G: Grid, and any interaction with the grid happens via the trait. Your trait will likely need more functions than in my simplistic example.
The immediate issue you're running into is that Rust's HashMap requires its keys to be Hash and Eq, and the closure you provide does not implement either trait. Function pointers (fn(T, U) -> V) are Hash and Eq, but less flexible than closures.
Even if closures could implement Hash and Eq, you couldn't make them useful as HashMap keys. From the Rust reference, ยง10.1.12:
A closure expression produces a closure value with a unique, anonymous type that cannot be written out. [emphasis added]
In other words, no two closures can have the same type, even if their contents are identical! As a result, the types of any two closures you attempt to add as keys will conflict.
I'm not too familiar with wave function collapse (which you mention in your comment), but a solution here could be to use some type that describes the function as the key, rather than the function itself. Take this very simple example:
#[derive(PartialEq, Eq, Hash)]
pub struct KeyFunction {
to_add: i32,
}
impl KeyFunction {
pub fn apply(&self, param: &i32) -> i32 {
*param + self.to_add
}
}
type Ruleset<V> = HashMap<KeyFunction, V>;
This will limit the keys you can use in a particular ruleset based on what information the KeyFunction type stores. However, you could use a trait to genericize over different kinds of key function (playground link):
use std::collections::HashMap;
pub trait KeyFunction {
type Coord;
fn apply(&self, input: &Self::Coord) -> Self::Coord;
}
/// Descriptor type for simple, contextless 2-D key functions.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct D2KeyFunction {
x_ofs: i32,
y_ofs: i32,
}
impl KeyFunction for D2KeyFunction {
type Coord = (i32, i32);
fn apply(&self, input: &Self::Coord) -> Self::Coord {
(input.0 + self.x_ofs, input.1 + self.y_ofs)
}
}
type D2Ruleset<V> = HashMap<D2KeyFunction, V>;
This allows you to have flexible key functions while working within Rust's type system, and also enforces boundaries between incompatible key functions.

How do I create a HashMap with type erased keys?

I want to be able to use a variety of different types as keys in a HashMap, all of which would implement Hash. This seems like it should be possible: from reading the docs it seems that every Hasher will produce a u64 result, so they eventually get reduced down to a common type. Effectively I want to do:
use std::{collections::HashMap, hash::Hash};
fn x(_: HashMap<Box<dyn Hash>, ()>) {}
which I'm not allowed to do:
error[E0038]: the trait `std::hash::Hash` cannot be made into an object
--> src/lib.rs:3:9
|
3 | fn x(_: HashMap<Box<dyn Hash>, ()>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::hash::Hash` cannot be made into an object
It seems like I could create a Hasher (e.g. RandomState), use that to manually calculate hash values, then store the u64 result in a HashMap<u64, _> but that seems kind of overly complex. I don't ever want to get the key values back out again, I just need to be able to compare the hash values. Is there a HashMap alternative I'm not aware of? Or am I looking at this in a totally wrong way?
It seems like I could create a Hasher (e.g. RandomState), use that to manually calculate hash values, then store the u64 result in a HashMap<u64, _> but that seems kind of overly complex.
Unfortunately that's overly simple - since a hash function discards some information, hash tables don't just work on hashes, they also need the original key to compare it with the key you're inserting or looking for. This is necessary to handle the case when two keys produce the same hash, which is both possible and allowed.
Having said that, you can implement Python-style hash tables with heterogeneous keys in Rust, but you'll need to box the keys, i.e. use Box<dyn Key> as the type-erased key, with Key being a trait you define for all concrete types you want to use as the hash key. In particular, you'll need to:
define the Key trait that specifies how to compare and hash the actual keys;
(optionally) provide a blanket implementation of that trait for types that are themselves Hash and Eq so the user doesn't need to do that for every type manually;
define Eq and Hash for Box<dyn Key> using the methods the trait provides, making Box<dyn Key> usable as key in std::collections::HashMap.
The Key trait could be defined like this:
trait Key {
fn eq(&self, other: &dyn Key) -> bool;
fn hash(&self) -> u64;
// see https://stackoverflow.com/a/33687996/1600898
fn as_any(&self) -> &dyn Any;
}
And here is a blanket implementation of Key for any type that is Eq and Hash:
use std::any::{Any, TypeId};
use std::collections::{HashMap, hash_map::DefaultHasher};
use std::hash::{Hash, Hasher};
impl<T: Eq + Hash + 'static> Key for T {
fn eq(&self, other: &dyn Key) -> bool {
if let Some(other) = other.as_any().downcast_ref::<T>() {
return self == other;
}
false
}
fn hash(&self) -> u64 {
let mut h = DefaultHasher::new();
// mix the typeid of T into the hash to make distinct types
// provide distinct hashes
Hash::hash(&(TypeId::of::<T>(), self), &mut h);
h.finish()
}
fn as_any(&self) -> &dyn Any {
self
}
}
Finally, these impls will make Box<dyn Key> usable as hash table keys:
impl PartialEq for Box<dyn Key> {
fn eq(&self, other: &Self) -> bool {
Key::eq(self.as_ref(), other.as_ref())
}
}
impl Eq for Box<dyn Key> {}
impl Hash for Box<dyn Key> {
fn hash<H: Hasher>(&self, state: &mut H) {
let key_hash = Key::hash(self.as_ref());
state.write_u64(key_hash);
}
}
// just a convenience function to box the key
fn into_key(key: impl Eq + Hash + 'static) -> Box<dyn Key> {
Box::new(key)
}
With all that in place, you can use the keys almost as you would in a dynamic language:
fn main() {
let mut map = HashMap::new();
map.insert(into_key(1u32), 10);
map.insert(into_key("foo"), 20);
map.insert(into_key("bar".to_string()), 30);
assert_eq!(map.get(&into_key(1u32)), Some(&10));
assert_eq!(map.get(&into_key("foo")), Some(&20));
assert_eq!(map.get(&into_key("bar".to_string())), Some(&30));
}
Playground
Note that this implementation will always consider values of distinct concrete types to have different values. While Python's dictionaries will consider keys 1 and 1.0 to be the same key (while "1" will be distinct), an into_key(1u32) will be distinct not just from into_key(1.0), but also from into_key(1u64). Likewise, into_key("foo") will be a distinct from into_key("foo".to_string()). This could be changed by manually implementing Key for the types you care about, in which case the blanket implementation must be removed.
I could not understand if you need to compare 2 hash or insert a hash into a HashMap, so i'm going to give you aswears to both.
This generic function generate hash for any given type.
fn hash<T>(obj: T) -> u64
where
T: Hash,
{
let mut hasher = DefaultHasher::new();
obj.hash(&mut hasher);
hasher.finish()
}
To compare you would do something like this :
let string = String::from("hello");
let _i32 = 12;
let result = hash(&string) == hash(_i32);
println!("{} == {} -> {}", &string, _i32, result);
This also work for struct :
#[derive(Hash, Debug)]
struct Person{
name : String,
age : i32
}
let person1 = Person{name : "Kelvin".to_string(), age: 19};
let person2 = Person{name : "Dmitri".to_string(), age: 17};
let result = hash(&person1) == hash(&person2);
println!("{:?} == {:?} -> {}", &person1, &person2, result);
To insert Hash into a HashMap :
let mut HashMap : HashMap<u64, ()> = HashMap::new();
HashMap.insert(hash("hello world"), ());

Resources