Created
March 23, 2021 12:02
-
-
Save JensAyton/4531449ae5c2ec42604cabc3c5fc4a3f to your computer and use it in GitHub Desktop.
A stab at implementing AnyEquatable with implicit conversions, leveraging AnyHashable. Unfortunately it’s not possible to get the full magic of AnyHashable, in particular with type-erased types.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
public struct AnyEquatable: Equatable { | |
private struct EquatableBox<T: Equatable>: Hashable { | |
let value: T | |
func hash(into hasher: inout Hasher) { | |
preconditionFailure("EquatableBox must never be hashed") | |
} | |
} | |
public let base: Any | |
private let hashable: AnyHashable | |
public init<T: Equatable>(_ value: T) { | |
base = value | |
hashable = AnyHashable(EquatableBox(value: value)) | |
} | |
// This overload is required for cases like NSString - String equality to work as intended. Unfortunately, this | |
// means that type-erased NSStrings (("Foo" as NSString) as Any) won’t be handled correctly. | |
// | |
// AnyHashable works around this with a combination of underscored protocols and special runtime functions. | |
public init<T: Hashable>(_ value: T) { | |
base = value | |
hashable = AnyHashable(value) | |
} | |
public init(_ value: AnyHashable) { | |
base = value | |
hashable = value | |
} | |
public init(_ value: AnyEquatable) { | |
self = value | |
} | |
public static func == (lhs: Self, rhs: Self) -> Bool { | |
return lhs.hashable == rhs.hashable | |
} | |
} | |
struct S: Equatable { | |
let value: String | |
} | |
struct I: Equatable { | |
let value: Int | |
} | |
struct I8: Equatable { | |
let value: Int8 | |
} | |
let string1 = AnyEquatable(S(value: "Hello")) | |
let string2 = AnyEquatable(S(value: "Hello")) | |
let string3 = AnyEquatable(S(value: "30")) | |
let int1 = AnyEquatable(I(value: 10)) | |
let int2 = AnyEquatable(I(value: 10)) | |
let int3 = AnyEquatable(I(value: 30)) | |
let i8 = AnyEquatable(I(value: 10)) | |
string1 == string2 // true | |
string1 == string3 // false | |
int1 == int2 // true | |
int1 == int3 // false | |
string3 == int3 // false | |
i8 == int1 // true | |
i8 == int3 // false | |
let string = AnyEquatable("Hello" as String) | |
let nsString = AnyEquatable("Hello" as NSString) | |
nsString == string // true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment