-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathEquatable.swift
305 lines (297 loc) · 11.3 KB
/
Equatable.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Equatable
//===----------------------------------------------------------------------===//
/// A type that can be compared for value equality.
///
/// Types that conform to the `Equatable` protocol can be compared for equality
/// using the equal-to operator (`==`) or inequality using the not-equal-to
/// operator (`!=`). Most basic types in the Swift standard library conform to
/// `Equatable`.
///
/// Some sequence and collection operations can be used more simply when the
/// elements conform to `Equatable`. For example, to check whether an array
/// contains a particular value, you can pass the value itself to the
/// `contains(_:)` method when the array's element conforms to `Equatable`
/// instead of providing a closure that determines equivalence. The following
/// example shows how the `contains(_:)` method can be used with an array of
/// strings.
///
/// let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
///
/// let nameToCheck = "Kofi"
/// if students.contains(nameToCheck) {
/// print("\(nameToCheck) is signed up!")
/// } else {
/// print("No record of \(nameToCheck).")
/// }
/// // Prints "Kofi is signed up!"
///
/// Conforming to the Equatable Protocol
/// ====================================
///
/// Adding `Equatable` conformance to your custom types means that you can use
/// more convenient APIs when searching for particular instances in a
/// collection. `Equatable` is also the base protocol for the `Hashable` and
/// `Comparable` protocols, which allow more uses of your custom type, such as
/// constructing sets or sorting the elements of a collection.
///
/// You can rely on automatic synthesis of the `Equatable` protocol's
/// requirements for a custom type when you declare `Equatable` conformance in
/// the type's original declaration and your type meets these criteria:
///
/// - For a `struct`, all its stored properties must conform to `Equatable`.
/// - For an `enum`, all its associated values must conform to `Equatable`. (An
/// `enum` without associated values has `Equatable` conformance even
/// without the declaration.)
///
/// To customize your type's `Equatable` conformance, to adopt `Equatable` in a
/// type that doesn't meet the criteria listed above, or to extend an existing
/// type to conform to `Equatable`, implement the equal-to operator (`==`) as
/// a static method of your type. The standard library provides an
/// implementation for the not-equal-to operator (`!=`) for any `Equatable`
/// type, which calls the custom `==` function and negates its result.
///
/// As an example, consider a `StreetAddress` class that holds the parts of a
/// street address: a house or building number, the street name, and an
/// optional unit number. Here's the initial declaration of the
/// `StreetAddress` type:
///
/// class StreetAddress {
/// let number: String
/// let street: String
/// let unit: String?
///
/// init(_ number: String, _ street: String, unit: String? = nil) {
/// self.number = number
/// self.street = street
/// self.unit = unit
/// }
/// }
///
/// Now suppose you have an array of addresses that you need to check for a
/// particular address. To use the `contains(_:)` method without including a
/// closure in each call, extend the `StreetAddress` type to conform to
/// `Equatable`.
///
/// extension StreetAddress: Equatable {
/// static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool {
/// return
/// lhs.number == rhs.number &&
/// lhs.street == rhs.street &&
/// lhs.unit == rhs.unit
/// }
/// }
///
/// The `StreetAddress` type now conforms to `Equatable`. You can use `==` to
/// check for equality between any two instances or call the
/// `Equatable`-constrained `contains(_:)` method.
///
/// let addresses = [StreetAddress("1490", "Grove Street"),
/// StreetAddress("2119", "Maple Avenue"),
/// StreetAddress("1400", "16th Street")]
/// let home = StreetAddress("1400", "16th Street")
///
/// print(addresses[0] == home)
/// // Prints "false"
/// print(addresses.contains(home))
/// // Prints "true"
///
/// Equality implies substitutability---any two instances that compare equally
/// can be used interchangeably in any code that depends on their values. To
/// maintain substitutability, the `==` operator should take into account all
/// visible aspects of an `Equatable` type. Exposing nonvalue aspects of
/// `Equatable` types other than class identity is discouraged, and any that
/// *are* exposed should be explicitly pointed out in documentation.
///
/// Since equality between instances of `Equatable` types is an equivalence
/// relation, any of your custom types that conform to `Equatable` must
/// satisfy three conditions, for any values `a`, `b`, and `c`:
///
/// - `a == a` is always `true` (Reflexivity)
/// - `a == b` implies `b == a` (Symmetry)
/// - `a == b` and `b == c` implies `a == c` (Transitivity)
///
/// Moreover, inequality is the inverse of equality, so any custom
/// implementation of the `!=` operator must guarantee that `a != b` implies
/// `!(a == b)`. The default implementation of the `!=` operator function
/// satisfies this requirement.
///
/// Equality is Separate From Identity
/// ----------------------------------
///
/// The identity of a class instance is not part of an instance's value.
/// Consider a class called `IntegerRef` that wraps an integer value. Here's
/// the definition for `IntegerRef` and the `==` function that makes it
/// conform to `Equatable`:
///
/// class IntegerRef: Equatable {
/// let value: Int
/// init(_ value: Int) {
/// self.value = value
/// }
///
/// static func == (lhs: IntegerRef, rhs: IntegerRef) -> Bool {
/// return lhs.value == rhs.value
/// }
/// }
///
/// The implementation of the `==` function returns the same value whether its
/// two arguments are the same instance or are two different instances with
/// the same integer stored in their `value` properties. For example:
///
/// let a = IntegerRef(100)
/// let b = IntegerRef(100)
///
/// print(a == a, a == b, separator: ", ")
/// // Prints "true, true"
///
/// Class instance identity, on the other hand, is compared using the
/// triple-equals identical-to operator (`===`). For example:
///
/// let c = a
/// print(c === a, c === b, separator: ", ")
/// // Prints "true, false"
public protocol Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func == (lhs: Self, rhs: Self) -> Bool
}
extension Equatable {
/// Returns a Boolean value indicating whether two values are not equal.
///
/// Inequality is the inverse of equality. For any values `a` and `b`, `a != b`
/// implies that `a == b` is `false`.
///
/// This is the default implementation of the not-equal-to operator (`!=`)
/// for any type that conforms to `Equatable`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
// transparent because sometimes types that use this generate compile-time
// warnings, e.g. that an expression always evaluates to true
@_transparent
public static func != (lhs: Self, rhs: Self) -> Bool {
return !(lhs == rhs)
}
}
// Called by the SwiftValue implementation.
@_silgen_name("_swift_stdlib_Equatable_isEqual_indirect")
internal func Equatable_isEqual_indirect<T: Equatable>(
_ lhs: UnsafePointer<T>,
_ rhs: UnsafePointer<T>
) -> Bool {
return lhs.pointee == rhs.pointee
}
//===----------------------------------------------------------------------===//
// Reference comparison
//===----------------------------------------------------------------------===//
/// Returns a Boolean value indicating whether two references point to the same
/// object instance.
///
/// This operator tests whether two instances have the same identity, not the
/// same value. For value equality, see the equal-to operator (`==`) and the
/// `Equatable` protocol.
///
/// The following example defines an `IntegerRef` type, an integer type with
/// reference semantics.
///
/// class IntegerRef: Equatable {
/// let value: Int
/// init(_ value: Int) {
/// self.value = value
/// }
/// }
///
/// func == (lhs: IntegerRef, rhs: IntegerRef) -> Bool {
/// return lhs.value == rhs.value
/// }
///
/// Because `IntegerRef` is a class, its instances can be compared using the
/// identical-to operator (`===`). In addition, because `IntegerRef` conforms
/// to the `Equatable` protocol, instances can also be compared using the
/// equal-to operator (`==`).
///
/// let a = IntegerRef(10)
/// let b = a
/// print(a == b)
/// // Prints "true"
/// print(a === b)
/// // Prints "true"
///
/// The identical-to operator (`===`) returns `false` when comparing two
/// references to different object instances, even if the two instances have
/// the same value.
///
/// let c = IntegerRef(10)
/// print(a == c)
/// // Prints "true"
/// print(a === c)
/// // Prints "false"
///
/// - Parameters:
/// - lhs: A reference to compare.
/// - rhs: Another reference to compare.
#if !$Embedded
@inlinable // trivial-implementation
public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return ObjectIdentifier(l) == ObjectIdentifier(r)
case (nil, nil):
return true
default:
return false
}
}
#else
@inlinable // trivial-implementation
public func ===<T: AnyObject, U: AnyObject>(lhs: T?, rhs: U?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return Builtin.bridgeToRawPointer(l) == Builtin.bridgeToRawPointer(r)
case (nil, nil):
return true
default:
return false
}
}
#endif
/// Returns a Boolean value indicating whether two references point to
/// different object instances.
///
/// This operator tests whether two instances have different identities, not
/// different values. For value inequality, see the not-equal-to operator
/// (`!=`) and the `Equatable` protocol.
///
/// - Parameters:
/// - lhs: A reference to compare.
/// - rhs: Another reference to compare.
#if !$Embedded
@inlinable // trivial-implementation
public func !== (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
return !(lhs === rhs)
}
#else
@inlinable // trivial-implementation
public func !==<T: AnyObject, U: AnyObject>(lhs: T, rhs: U) -> Bool {
return !(lhs === rhs)
}
#endif