Search code examples
swiftstructiequatable

swift - Comparing structs that conform to a protocol


I have the following structs that represent a point or a line:

public struct Point{
    let x : Double
    let y : Double

    init (x : Double, y : Double)
    {
        self.x = x
        self.y = y
    }
}
extension Point : Equatable{}
    public func ==(lhs: Point, rhs: Point) -> Bool
    {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

And

public struct Line {
    let points : [Point]
    init(points : [Point])
    {
        self.points = points
    }
}

extension Line : Equatable {}
    public func ==(lhs: Line, rhs: Line) -> Bool
    {
        return lhs.points == rhs.points
    }

I want to be able to have a Shape protocol or struct that I can use to have Points and Lines and then I can compare between them. I tried to that with conforming protocol Shape but Swift compiler gives me an error when I want to compare a point with a line even though they are Shapes.

Do I have to move from struct to classes?
I think I may have to use generics but don't know exactly how to solve this issue. Thanks in advance for any guidance.

Edit1:

My approach to Shape protocol was really just trying stuff but nothing worked. I tried the following:

protocol MapShape : Equatable
{
      func == (lhs: MapShape, rhs: MapShape ) -> Bool
}

I also changed the code for the Equatable extension for lines given the suggestion


Solution

  • This topic is covered in the WWDC 2015 session video Protocol-Oriented Programming in Swift, and here is my attempt to apply that to your situation:

    You define a protocol Shape and a protocol extension method isEqualTo::

    protocol Shape {
        func isEqualTo(other: Shape) -> Bool
    }
    
    extension Shape where Self : Equatable {
        func isEqualTo(other: Shape) -> Bool {
        if let o = other as? Self { return self == o }
        return false
        }
    }
    

    isEqualTo: checks if the other element is of the same type (and compares them with == in that case), and returns false if they are of different type.

    All types which are Equatable automatically conform to Shape, so that we can just set

    extension Point : Shape { }
    extension Line : Shape { }
    

    (Of course you can add other methods to Shape which should be implemented by all shape types.)

    Now we can define == for shapes as

    func ==(lhs: Shape, rhs: Shape) -> Bool {
            return lhs.isEqualTo(rhs)
    }
    

    and voilà, points and lines can be compared:

    let p1 = Point(x: 1, y: 2)
    let p2 = Point(x: 1, y: 3)
    let l1 = Line(points: [p1, p2])
    let l2 = Line(points: [p1, p2])
    
    print(p1 == p2) // false
    print(p1 == l1) // false
    print(l1 == l2) // true
    

    Remark: You have == for the Shape type now, but I haven't figured out yet to how make Shape conform to Equatable. Perhaps someone else can solve that part (if it is possible).