Generic parameter 'Self' where none is defined

I'm having some trouble wrapping my head around why the following code fails to compile:

protocol P {
    var p: String { get }
}

extension P {
    static func == (lhs: P, rhs: P) -> Bool {
        lhs.p == rhs.p
    }
}

struct PS: P {
    let p: String
}

PS(p: "a") == PS(p: "b") // Generic parameter 'Self' could not be inferred
  • There does not appear to be any parameter with a type dependency on Self.
  • I thought perhaps Equatable is somehow involved, since it does depend on Self, but AFAIK I am not involving Equatable in any way.
  • The only type information necessary to perform the requested actions is the abstract protocol type information that is available by simply typing lhs and rhs as P.
  • I don't see any need for having access to concrete type information.

Your == operator, defined in a protocol extension, exists concretely on every type conforming to P, and nowhere else.

There is no “free-standing” operator P.==. Instead there is only PS.== (and similarly for any other type conforming to P).

So when you try to use == with two operands of type PS, the compiler does not know which version of == to use. It tries to use the == function defined in the extension of P, but in order to do so it needs to know which conforming type’s version of that operator to use.

In other words, the compiler needs to know the type of Self in order to call Self.==, but you have not provided any constraints to specify Self on the == function, so Self could be any type conforming to P, and the compiler has no way to choose one of them.

If you want a free-standing operator, rather than a per-conforming-type operator, then you need to put your operator function at the top-level (global scope), rather than nesting it inside of P.

4 Likes

To be even more specific, the generic signature of the == declaration looks like the following:

<Self where Self : P> (Self.Type) -> (P, P) -> Bool

If == were declared as static func ==(lhs: Self, rhs: Self) -> Bool, then the compiler would know that the type of the arguments is the same as the type of the hidden generic parameter Self. However, as declared, there is no connection between the arguments supplied to == and the type of Self.

Now, in this specific case, P.== doesn't reference Self at all so the behavior doesn't depend on it, but consider the following slight modification:

    static func == (lhs: P, rhs: P) -> Bool {
        print(Self.self)
        return lhs.p == rhs.p
    }

Now, even though the signature doesn't depend on Self, the implementation does. It would probably be pretty surprising for library authors if the above change turned out to be source-breaking (after all, whether a given implementation references Self sure looks like an implementation detail), so we require Self to be fully specified when referencing protocol members even if it isn't used in the body.

5 Likes
Terms of Service

Privacy Policy

Cookie Policy