1
votes

I'm trying to create a method that takes an array of structs that conform to a protocol in Swift.

For the simplest example of this, I define an empty protocol and a method that takes an array of objects conforming to that protocol and just prints them

protocol SomeProtocol {}

func methodTakingProtocol(objects: [SomeProtocol]) {
    // do something with the array of objects
    print(objects)
}

When I try to feed this method an array of structs that conform to SomeProtocol, however, I get an error

struct SomeStruct: SomeProtocol {}

let arrayOfStructs = [ SomeStruct(), SomeStruct() ]

methodTakingProtocol(arrayOfStructs)
// ^ "Cannot convert value of type '[SomeStruct]' to expected argument type '[SomeProtocol]'"

Poking around a little, I've found that I can get around this problem by explicitly calling out SomeStruct's adoption of SomeProtocol

let arrayOfStructs: [SomeProtocol] = [ SomeStruct(), SomeStruct() ]

// This will work
methodTakingProtocol(arrayOfStructs)

Can someone tell me what's going on here? Is this a bug that I should file a radar for, or is there some reasoning as to why the compiler doesn't recognize this array of structs as conforming to the protocol they have been marked as adopting?

1
I don't think this is a bug. The compiler sees instances of SomeStruct and therefore sets the type of the array to [SomeStruct]. To change the type, you need to make your intent explicit. You might file a feature radar, though. - dasdom

1 Answers

0
votes

This is actually working as intended. In order to pass the array to the method you have to either cast it or explicitly declare it as the protocol:

protocol SomeProtocol {}

struct SomeStruct: SomeProtocol {}

// explicitly typed
let arrayOfStructs:[SomeProtocol] = [ SomeStruct(), SomeStruct() ]

func foo(bar:[SomeProtocol]) { }

foo(arrayOfStructs) // Works!

Here is an excellent article on this topic: Generic Protocols & Their Shortcomings

But that begs the question; Why can't we use generic protocols outside of generic constraints?

The short answer is: Swift wants to be type-safe. Couple that with the fact that it's an ahead-of-time compiled language, and you have a language that NEEDS to be able to infer a concrete type at anytime during compilation. I can't stress that enough. At compile time, every one of your types that aren't function/class constraints need to be concrete. Associated types within a protocol are abstract. Which means they aren't concrete. They're fake. And no one likes a fake.

edit: It's still a great article but upon re-reading I realized that it doesn't exactly apply here since we're discussing "concrete protocols" and not "generic protocols".