Equality is broken, isn't it?


(Alexey Komnin) #1

Here is the code:

    let a: String = "abc"
    let b: NSString = "abc"

    assert(a == b)
    assert(a.hashValue == b.hashValue, "a.hashValue(\(a.hashValue)) !=
b.hashValue(\(b.hashValue))")

It fails with error:
    assertion failed: a.hashValue(4799450059707601744) != b.hashValue(516202353)

Perhaps, there is an issue with the equality operator.

Alexey Komnin.


(Dmitri Gribenko) #2

String and NSString use different comparison algorithms, and thus use
incompatible hash codes.

When you issue a mixed-type comparison, one is bridged to the other.

Dmitri

···

On Wed, Jul 6, 2016 at 9:54 AM, Alexey Komnin via swift-users <swift-users@swift.org> wrote:

Here is the code:

    let a: String = "abc"
    let b: NSString = "abc"

    assert(a == b)
    assert(a.hashValue == b.hashValue, "a.hashValue(\(a.hashValue)) !=
b.hashValue(\(b.hashValue))")

It fails with error:
    assertion failed: a.hashValue(4799450059707601744) != b.hashValue(516202353)

Perhaps, there is an issue with the equality operator.

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Jens Alfke) #3

Try comparing
  (a as NSString).hashValue == b.hashValue
or
  a.hashValue == (b as String).hashValue

It’s true that equal objects should have equal hash codes, but that really only applies when the objects have the same type so there’s no implicit coercion going on. For example, in scripting languages where “1” == 1 (pretty sure this is true of JavaScript and Perl, for example) it’s probably not true that “1”.hashCode == 1.hashCode.

In a Swift Dictionary, if you give the item type as String or NSString then everything’s converted to the same type when added, so hashCode will work as you expect. And if you use a Dictionary<AnyObject>, there won’t be any implicit String<->NSString conversions going on, so if you added both your ‘a’ and ‘b’ objects I believe they would be stored separately as distinct keys.

(The moral is that the distinction between String and NSString is kind of messy, unfortunately. But the implicit conversion has been pretty important for Swift adoption given how ubiquitous NSString is in Cocoa APIs and existing apps.)

—Jens

···

On Jul 6, 2016, at 9:54 AM, Alexey Komnin via swift-users <swift-users@swift.org> wrote:

Here is the code:

   let a: String = "abc"
   let b: NSString = "abc"

   assert(a == b)
   assert(a.hashValue == b.hashValue, "a.hashValue(\(a.hashValue)) !=
b.hashValue(\(b.hashValue))”)


(Jeff Kelley) #4

I wouldn’t expect String and NSString to have identical implementations of hashValue(). Is there a problem you’re having that this example is meant to illustrate? I can see this being an issue if you’re building your own collection type for strings.

Jeff Kelley

SlaunchaMan@gmail.com | @SlaunchaMan <https://twitter.com/SlaunchaMan> | jeffkelley.org <http://jeffkelley.org/>

···

On Jul 6, 2016, at 12:54 PM, Alexey Komnin via swift-users <swift-users@swift.org> wrote:

Here is the code:

   let a: String = "abc"
   let b: NSString = "abc"

   assert(a == b)
   assert(a.hashValue == b.hashValue, "a.hashValue(\(a.hashValue)) !=
b.hashValue(\(b.hashValue))")

It fails with error:
   assertion failed: a.hashValue(4799450059707601744) != b.hashValue(516202353)

Perhaps, there is an issue with the equality operator.

Alexey Komnin.


(Brent Royal-Gordon) #5

There's no problem if you use generics to select a single Hashable implementation:

  import Foundation
  
  func assertHashableConsistent<T: Hashable>(a: T, b: T) {
      assert(a == b, "a and b are equal")
      assert(a.hashValue == b.hashValue, "a and b have the same hash value")
  }
  
  assertHashableConsistent(a: "abc" as String, b: "abc" as NSString)

The problem is that, in your case, `a` uses `NSString`'s `Hashable` in the first line, but `String`'s `Hashable` in the second line. The `assertHashableConsistent(a:b:)` function, on the other hand, ensures that `a` uses `NSString`'s `Hashable` in both lines.

···

On Jul 6, 2016, at 9:54 AM, Alexey Komnin via swift-users <swift-users@swift.org> wrote:

Here is the code:

   let a: String = "abc"
   let b: NSString = "abc"

   assert(a == b)
   assert(a.hashValue == b.hashValue, "a.hashValue(\(a.hashValue)) !=
b.hashValue(\(b.hashValue))")

--
Brent Royal-Gordon
Architechies