Search code examples
swiftswift-extensionsswift-protocols

Swift Array extension with where clause doesn't work on sub-protocols


I've written some code that I think ought to work in Swift 2 (Xcode 7b4), but it's not compiling. I'm hoping to get some thoughts on whether what I'm trying to do should be valid.

Consider this example Array extension:

extension Array where Element: AnyObject {
    mutating func appendUniqueInstance(e: Element) {
        for element in self {
            if (element === e) {
                return
            }
        }

        self.append(e)
    }
}

First, what do I mean by an Array whose elements are AnyObject? Basically I'm saying the array should contain a heterogeneous group of non-value-type objects that can be compared for instance equality (===).

The example function appendUniqueInstance() only inserts an element into an array if it isn't already in the array. This is similar to a Set insert() operation but obviously provides ordering and (more importantly) does not impose Set's homogeneous-type requirement (via Equatable's use of Self).

If I now define a protocol P and a class C that implements P:

protocol P : AnyObject {}

class C : P {}

and instantiate a C:

let c = C()

Then these very obvious things are true:

let cIsAnyObject = c as AnyObject   // ok
let cIsP = c as P                   // ok

And I can now do the following:

var a1 = [AnyObject]()              // []

a1.appendUniqueInstance(c)          // [c]
a1.appendUniqueInstance(c)          // [c]

So far, so good, but now for the problem case:

var a2 = [P]()

a2.append(c)                        // ok, -> [c]

// Compiler error: Cannot invoke 'appendUniqueInstance' with an argument list of type '(C)'
a2.appendUniqueInstance(c)

Here, a2 is typed as an array of P, so it should be a perfectly valid thing to do to append an instance of P, and indeed the line a2.append(c) works as we'd expect.

Calling the Array extension function appendUniqueInstance() however generates a compiler error.

As far as I can tell, the compiler seems to be getting confused about what can be passed to appendUniqueInstance() and not realizing (or allowing for some reason) that C (via P) is an AnyObject.

Incidentally, if I declare P instead as:

@objc protocol P : AnyObject {}

then everything compiles just fine, but I also then have to make sure everything in protocol P conforms to @objc, which is not what I want.

So, after all of this, my question is: does this seem like something that should work? I hope it isn't just a case of getting some declaration syntax wrong, but I guess I'll be glad if it is.


Solution

  • It appears the error reported by the compiler at the time I wrote the original question was simply misleading, and the functionality I was trying to obtain wasn't (and still isn't) supported by Swift.

    With Xcode 8.2.1 and Swift 3 a more accurate error is reported:

    // error: using 'P' as a concrete type conforming
    //   to protocol 'AnyObject' is not supported
    a2.appendUniqueInstance(c)