Search code examples
iosswiftswift-protocolsassociated-types

Why using Self as return type is not considered as the protocol's constraint?


This is a valid protocol declaration in Swift:

protocol Proto1: class {
    func method1() -> Self
    func method2() -> [Proto1]
}

But this isn't:

protocol Proto2: class {
    func method1(param: Self)
    func method2() -> [Proto2]
}

The error message is:

Protocol 'Proto2' can only be used as a generic constraint because it has Self or associated type requirements

So it seems that, when using Self as function's return type, Swift doesn't consider that as a constraint of the protocol being defined and hence it's OK to use the protocol itself as functions' return type. But when using Self as function's parameter type, the behavior is completely different.

I wonder why such a difference?


Solution

  • Because it's not useful to have a Self as a parameter type of a method. Suppose that you can do:

    protocol P {
        func f(_ x: Self)
    }
    
    class A: P {
        func f(_ x: Self) {
            // not relevant
        }
    }
    
    class B: A { }
    

    Now suppose I have:

    func g(x: A) {
        // what can I do with x?
    }
    

    The fact is, there is no way to call x.f. Because I can pass an instance of B to x, in which case x.f would accept a B instead. x could be an instance of any subclass of A that I have no way of knowing at compile time, so I don't know what I can pass to x.f.


    Compare that to Self used as the return type:

    protocol P {
        func f() -> Self
    }
    
    // Implementation:
    class A: P {
        func f() -> Self {
            self
        }
    }
    
    class B: A { }
    
    func g(x: A) {
        let foo: A = x.f()
    }
    

    Here, we know that I can at least assign the return value of x.f to a variable of type A. Even if x is an instance of B, which means that f returns a B, we can still assign that to a variable of type A.