Whenever you go beyond classical protocol oriented programming practices, and use protocols for a variety of functionality across your code; you face this noted error with Swift. Next, you suddenly change most of your implementation or try to workaround it by searching solutions online.

I guess understanding the reasoning behind this error leads to better software design before implementation. Let’s say we have a Comparable protocol as:

protocol Comparable {
    func compare(other: Self) -> Bool
}

Self is an associated type. Associated types (as an instance variable or a function argument) are decided by the implementation that associates with them. Comparable protocol has appearently Self (or associated type) requirement. You cannot have a direct reference to it and call its functions.

To understand why, we will create Animal and Person structs conforming the protocol:

struct Animal: Comparable {
    let age: Int

    func compare(other: Self) -> Bool {
        return age > other.age
    }
}

struct Person: Comparable {
    let name: String

    func compare(other: Person) -> Bool {
        return name > other.name
    }
}

Animals are being compared by their ages and Person instances are being compared by alphabetical positions. Totally makes sense for our example. Finally if you want to write a function to sort any Comparable list with comparing them:

struct Utility {
    static func sortedComparables(_ comparables: [Comparable]) -> [Comparable] {
        return comparables.sorted {
            return $0.compare(other: $1)
        }
    }
}

From the protocol point of view, this should be legal. But how the compiler will compare age and name values of two different Comparable implementations Animal and Person.

Here we get a compiler error as error: protocol ‘Comparable’ can only be used as a generic constraint because it has Self or associated type requirements. Although it’s not visible at first look, the reason of this error is that if you have a reference to an object via a Comparable pointer instance, you will have an opportunity to call its functions.

Within the sort function let’s take the first element in array to operate on it:

let firstComparable = comparables.first

// What `Comparable` instance can you pass as an argument to this function? 
// It has to be guaranteed to compare two instances with same 
// concrete type (`Animal` or `Person`). There's no chance to have it here!
firstComparable.compare(...)

Any implementation that results in a reference pointer of a Comparable instance directly will produce this error.

Solution

See the first section of the error: protocol ‘Comparable’ can only be used as a generic constraint. Generics allow us to decide the function/class parameters at function call time. Thus we will be able to force any function parameter to a specific concrete type. Let’s fix the issue in Utility function:

struct Utility {
    static func sortedComparables<T: Comparable>(_ comparables: [T]) -> [T] {
        return comparables.sorted {
            return $0.compare(other: $1)
        }
    }
}

Here whenever you make a call to sortedComparables function, compiler will force you to pass an array of Comparables with the same concrete type: only Animal or only Person list. Now we are using Comparable as a generic constraint and satisfy the compiler.