Are you okay with the fact that two different types, but with the same name and size would be considered equal?
Are you okay with the fact that if you add a third property to a Circle it wouldn't be considered when comparing two circles?
Are you okay with Shape not being equatable itself (for example you cannot compare two arrays of Shapes)
If you're okay with all these disadvantages, then it's reasonable.
Usually the solution is to make a type-erasing wrapper, check out AnyHashable, or AnyView in SwifUI (There are multiple ways to write a type-erasing wrapper, using a closure is the easiest one)
protocol Shape: Equatable {
var name : String { get }
var size : Int { get }
}
struct AnyShape: Shape {
let base: Any // you don't have to provide this property, but it's often handy
let name: String
let size: Int
private let compare: (Any) -> Bool
init<T: Shape>(_ shape: T) {
self.base = shape
self.name = shape.name
self.size = shape.size
self.compare = {
guard let other = $0 as? T else { return false }
return shape == other
}
}
static func == (lhs: AnyShape, rhs: AnyShape) -> Bool {
lhs.compare(rhs.base)
}
}
struct Circle : Shape {
let name = "Circle"
var size : Int
}
struct Square : Shape {
let name = "Square"
var size : Int
}
let s1 = Square(size: 3)
let c1 = Circle(size: 3)
var shapes = [AnyShape(s1), AnyShape(c1)]
shapes == shapes
AnyShape(s1) == AnyShape(c1)
Pretty cool that you can create a stand alone == function and that would eliminate the problem of needing to be aware of Self
I think you made valid points about the limitations with my approach, I think the biggest limitation would be array comparison, I might need to build a == function for Sequence.
I am hardcoding name and making it let so that part is ok.