summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-02-25 23:45:37 -0800
committerGitHub <noreply@github.com>2021-02-26 08:45:37 +0100
commit63f1c38f4e0c6f8700a8877d5f3f6f2ac0fc16e6 (patch)
tree612126ea34cda446ff65ea6c7580d8afa324b091
parent4a31088ac29b6cc889d2c69e56f4aab72d8223de (diff)
downloadNim-63f1c38f4e0c6f8700a8877d5f3f6f2ac0fc16e6.tar.gz
hashes: support object default hash (#17175)
-rw-r--r--changelog.md4
-rw-r--r--lib/pure/hashes.nim30
-rw-r--r--tests/stdlib/thashes.nim46
3 files changed, 62 insertions, 18 deletions
diff --git a/changelog.md b/changelog.md
index 6b2c412f0..db5b3c14b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -173,14 +173,16 @@ provided by the operating system.
   dumping (on select signals) and notifying the parent process about the cause
   of termination.
 
-- Added `strip` and `setSlice` to `std/strbasics`.
+- `hashes.hash` now supports `object`, but can be overloaded.
 
+- Added `strip` and `setSlice` to `std/strbasics`.
 
 - Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`.
 
 - `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
   and `$none(int)` to `"none(int)"` instead of `"None[int]"`.
 
+
 ## Language changes
 
 - `nimscript` now handles `except Exception as e`.
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 54e083d25..c526c976f 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -54,20 +54,6 @@ runnableExamples:
 ## **Note:** If the type has a `==` operator, the following must hold:
 ## If two values compare equal, their hashes must also be equal.
 ##
-## You can hash an object by all of its fields with the `fields` iterator:
-runnableExamples:
-  proc hash(x: object): Hash =
-    for f in fields(x):
-      result = result !& hash(f)
-    result = !$result
-
-  type
-    Obj = object
-      x: int
-      y: string
-
-  doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2"))
-
 ## See also
 ## ========
 ## * `md5 module <md5.html>`_ for the MD5 checksum algorithm
@@ -499,14 +485,24 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
   result = !$h
 
 
-proc hash*[T: tuple](x: T): Hash =
-  ## Efficient hashing of tuples.
+proc hash*[T: tuple | object](x: T): Hash =
+  ## Efficient hashing of tuples and objects.
   ## There must be a `hash` proc defined for each of the field types.
+  runnableExamples:
+    type Obj = object
+      x: int
+      y: string
+    type Obj2[T] = object
+      x: int
+      y: string
+    assert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2"))
+    # you can define custom hashes for objects (even if they're generic):
+    proc hash(a: Obj2): Hash = hash((a.x))
+    assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2"))
   for f in fields(x):
     result = result !& hash(f)
   result = !$result
 
-
 proc hash*[A](x: openArray[A]): Hash =
   ## Efficient hashing of arrays and sequences.
   ## There must be a `hash` proc defined for the element type `A`.
diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim
index ce7bb7d8c..a4487c8c0 100644
--- a/tests/stdlib/thashes.nim
+++ b/tests/stdlib/thashes.nim
@@ -130,5 +130,51 @@ proc main() =
       # run at CT, expecting c semantics.
       discard
 
+  block: # hash(object)
+    type
+      Obj = object
+        x: int
+        y: string
+      Obj2[T] = object
+        x: int
+        y: string
+      Obj3 = object
+        x: int
+        y: string
+      Obj4 = object
+        case t: bool
+        of false:
+          x: int
+        of true:
+          y: int
+        z: int
+      Obj5 = object
+        case t: bool
+        of false:
+          x: int
+        of true:
+          y: int
+        z: int
+
+    proc hash(a: Obj2): Hash = hash(a.x)
+    proc hash(a: Obj3): Hash = hash((a.x,))
+    proc hash(a: Obj5): Hash =
+      case a.t
+      of false: hash(a.x)
+      of true: hash(a.y)
+
+    doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2"))
+    doAssert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2"))
+    doAssert hash(Obj2[float](x: 520, y: "Nim")) != hash(Obj2[float](x: 521, y: "Nim2"))
+    doAssert hash(Obj3(x: 520, y: "Nim")) == hash(Obj3(x: 520, y: "Nim2"))
+
+    doAssert hash(Obj4(t: false, x: 1)) == hash(Obj4(t: false, x: 1))
+    doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: false, x: 2))
+    doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: true, y: 1))
+
+    doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: false, x: 2))
+    doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1))
+    doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2))
+
 static: main()
 main()