0

Ideally I'd have the server implement the Equatable protocol but I ran into issues. Here's my code

protocol Server {
    var ipAddress: String { get }
    // simplified for this question
}

func ==<T:Server>(lhs: T, rhs: T) -> Bool {
    return lhs.ipAddress == rhs.ipAddress
}

func !=<T:Server>(lhs: T, rhs: T) -> Bool {
    return lhs.ipAddress != rhs.ipAddress
}

func ==<T:Server, U:Server>(lhs: T, rhs: U) -> Bool {
    return lhs.ipAddress == rhs.ipAddress
}

func !=<T:Server, U:Server>(lhs: T, rhs: U) -> Bool {
    return lhs.ipAddress != rhs.ipAddress
}

func doSomething(server0: Server, server1: Server) {
    // I want to compare to Server objects

    // !!! Compile Error !!!
    // Binary operator '==' cannot be applied to two 'Server' operands
    guard server0 == server1 else {
        print("SAME BAD")
        return
    }

    print("DO stuff")
}

Ultimately I just want to compare to abstract protocol objects against each other. Most of the other examples out there are comparing the concrete types.

Am I crazy for trying this or what? :P

Yogurt
  • 2,913
  • 2
  • 32
  • 63
  • 1
    Why not have `Server` conform to `Equatable`? – rmaddy Aug 16 '18 at 20:56
  • 1
    You have to be more careful with that. Multiple `ipAddresses` can be different strings, but are still the same server. E.g. `0:0:0:0:0:0:0:1` and `::1` – Alexander Aug 16 '18 at 21:02
  • 1
    @rmaddy Equatable adds a `Self` requirement, which is a total pain in the ass to work with. – Alexander Aug 16 '18 at 21:11
  • When hitting these kinds of issues, it's often useful to stop and think about whether a protocol is really needed – what different types would conform to `Server`, and what generic algorithms would you write across these types? You might find that `Server` is better expressed as a single structure that conforms to `Equatable`. – Hamish Aug 16 '18 at 22:36
  • @Alexander The server protocol was just an example, there are many other things used to distinguish a server, I removed those properties for simplicity. – Yogurt Aug 16 '18 at 23:26

1 Answers1

0

Your problem will go away if you make the function generic:

func doSomething<T1: Server, T2: Server>(server0: T1, server1: T2) {

This is required, because in Swift protocols don't conform to base protocols, not even to themselves. Adding the generic clause changes the function from being abstract to being concrete based on the arguments passed when called.

The same error happens for the following code:

struct A: Server {
    let ipAddress: String = ""
}

let server1: Server = A()
let server2: Server = A()
print(server1 == server2) // Binary operator '==' cannot be applied to two 'Server' operands

The error has the same cause: Server does not conform to Server (as odd as that might sound). This means that a function declaring to receive a protocol can't be called by passing a protocol to it, you need to tell the compiler which concrete implementation of the protocol you pass.

Cristik
  • 30,989
  • 25
  • 91
  • 127
  • I see, interesting link. Can I go over this a few questions with you, so that I can understand the static typing mechanism better? Why does swift require the concrete type information to be passed in, to make the comparison? It would appear that the information from the protocol definition would be enough. – Yogurt Aug 17 '18 at 00:23
  • @Biclops I added some more details to the answer, let me know if it answers your question – Cristik Aug 17 '18 at 03:48