Reputation: 22290
Let's say I have a simple Pet enum:
enum Pet {
case dog(name:String)
case cat(name:String)
var name:String {
switch self {
case .dog(let name):
return name
case .cat(let name):
return name
}
}
}
(The fact that they have the same type of associated value and therefore feels redundant should be ignored)
Now I want to be able to equate pets. I could write:
extension Pet:Equatable { }
func == (a:Pet, b:Pet) -> Bool {
return a.name == b.name
}
But this will allow a cat named "Spots" to be equal to a dog named "Spots". This I don't want. Is there an easy way to access the case of an enum instance? We usually do them in switch statements, but in this case, I wanted something more direct (one line), I might need to use it in a future Comparable extension after all.
I could do my own:
extension Pet {
var petType:Int {
switch self {
case .dog:
return 1
case .cat:
return 2
}
}
}
Now I can change my ==
implementation to be
func == (a:Pet, b:Pet) -> Bool {
return a.petType == b.petType && a.name == b.name
}
Is there some built in Swift syntax sugar that would do the same thing for me? I tried type(of:)
, but that just returns Pet, nothing about the instance case.
Upvotes: 0
Views: 549
Reputation: 17734
You can do something like this:
func ==(lhs: Pet, rhs: Pet) -> Bool {
if case let Pet.cat(l) = lhs, case let Pet.cat(r) = rhs where l == r { return true }
if case let Pet.dog(l) = lhs, case let Pet.dog(r) = rhs where l == r { return true }
return false
}
But a switch
probably looks better and is definitely easier to maintain.
Upvotes: 1
Reputation: 7756
I have sort of a hybrid approach that keeps things a tad more modular:
enum Pet {
case dog(name:String)
case cat(name:String)
var name: String {
switch self {
case .dog(let name):
return name
case .cat(let name):
return name
}
}
func equalCaseTo(otherPet: Pet) -> Bool {
switch (self, otherPet) {
case (.dog(_), .dog(_)), (.cat(_), .cat(_)):
return true
default:
return false
}
}
func equalNameTo(otherPet: Pet) -> Bool {
return name == otherPet.name
}
}
func ==(left:Pet, right:Pet) -> Bool {
return left.equalCaseTo(right) && left.equalNameTo(right)
}
Upvotes: 1
Reputation: 47906
As far as I know, Swift does not provide us a shortcut to retrieve only case labels.
In your case you can write something like this utilizing your name
property:
extension Pet: Equatable {
static func == (a: Pet, b: Pet) -> Bool {
switch (a, b) {
case (.dog, .dog), (.cat, .cat):
return a.name == b.name
default:
return false
}
}
}
If your Pet
does not have the name
property, you can write it as:
extension Pet: Equatable {
static func == (a: Pet, b: Pet) -> Bool {
switch (a, b) {
case let (.dog(namea), .dog(nameb)),
let (.cat(namea), .cat(nameb)):
return namea == nameb
default:
return false
}
}
}
But in your case, isn't it sort of natural to use class hierarchy?:
class Pet {
var name: String
init(name: String) {
self.name = name
}
}
class Dog: Pet {}
class Cat: Pet {}
extension Pet {
static func dog(name: String) -> Pet {
return Dog(name: name)
}
static func cat(name: String) -> Pet {
return Cat(name: name)
}
}
extension Pet: Equatable {
static func == (a: Pet, b: Pet) -> Bool {
return type(of: a) === type(of: b) && a.name == b.name
}
}
Upvotes: 2