How is opEquals used in toHash

Simen Kjærås simen.kjaras at gmail.com
Tue May 18 11:24:22 UTC 2021


On Tuesday, 18 May 2021 at 10:14:26 UTC, PinDPlugga wrote:
> But what I do not understand is why opEquals is necessary and 
> where in the implementation of toHash it plays its role? Since 
> area1 and area2 have different static arrays of Points I 
> understand why `typeid(points).getHash(&points)` would produce 
> different values for each, but it seems like the opEquals 
> should play a part in making them produce the same hash.

opEquals plays no part in how toHash does its thing, but it's 
important for how AAs work. When there's a hash collision, the 
colliding items are placed in a bucket, which is generally 
iterated linearly to find the matching element, using opEquals. 
Example code:

     ```d
     struct S {
         int i;
         size_t toHash() const nothrow @safe { return 3; }
         bool opEquals(ref const S rhs) const { return i == rhs.i; 
}
     }

     unittest {
         int[S] a;
         a[S(1)] = 3;
         a[S(2)] = 4; // Hash collision - both return 3
         assert(a.length == 2); // But they're both there
         assert(S(1) in a);
         assert(S(2) in a);
         int b = a[S(1)];
         int c = a[S(2)];
         assert(b == 3);
         assert(c == 4);
     }
     ```

And pseudocode for how an AA works:

     ```d
     struct AA(K, V) {
         import std.typecons : Tuple;
         alias Pair = Tuple!(K, "key", V, "value");
         alias Bucket = Pair[];

         Bucket[] buckets;

         this(int bucketCount) {
             buckets.length = bucketCount;
         }

         void opIndexAssign(V value, K key) {
             // Use hash to find correct bucket
             size_t hash = key.toHash();
             size_t index = hash % buckets.length;
             Bucket bucket = buckets[index];

             foreach (e; bucket) {
                 if (e.key == key) { // Check for duplicates with 
opEquals
                      e.value = value; // Overwrite existing
                      return;
                 }
             }

             bucket ~= Pair(key, value);
             // Array could reallocate when appended to,
             // so assign it back to the bucket list
             buckets[index] = bucket;
         }

         V opIndex(K key) {
             // Use hash to find correct bucket
             size_t hash = key.toHash();
             size_t index = hash % buckets.length;
             Bucket bucket = buckets[index];

             foreach (e; bucket) {
                 if (e.key == key) { // Check with opEquals
                     return e.value;
                 }
             }
             throw new Exception("Key not found");
         }
     }

     unittest {
         AA!(S, int) a = AA!(S, int)(24);
         a[S(1)] = 3;
         a[S(2)] = 4;
         assert(a[S(1)] == 3);
         assert(a[S(2)] == 4);
     }
     ```

This omits plenty of details, but should give some idea how AAs 
work.

--
   Simen


More information about the Digitalmars-d-learn mailing list