Jangles Jangles - 6 months ago 15
Swift Question

Swift: Passing conformed object instances to generic methods

I have the following playground:

// : Playground - noun: a place where people can play

import Foundation

// Define a protocol
protocol MyProtocol
{
func getMeAString() -> String!
}

// Create a class that conforms to the protocol
class ClassThatConformsToProtocol: MyProtocol
{
func getMeAString() -> String! {
return "hey!"
}
}

// Define a function that takes a generic, but ensure the generic conforms to the protocol
func logTheThing<T: MyProtocol>(theThing: T!)
{
theThing.getMeAString()
}

// Let's create an instance
let instanceOfClassThatConforms = ClassThatConformsToProtocol()


// We're now going to see if we can pass the object to our generic method
logTheThing(instanceOfClassThatConforms)

// It works!

// Let's create another method, but this one only knows the protocol its parameter conforms to, the in implementing class
func takeInAnObjectThatWeOnlyKnowItsProtocol(object:MyProtocol)
{
logTheThing(object) // error: cannot convert value of type 'MyProtocol' to expected argument type '_!'
}


As mentioned, I receive an error stating:


error: cannot convert value of type 'MyProtocol' to expected argument
type '_!'


For what reason would I not be able to pass this conformed object into the generic method?

If the compiler knows an object conforms to the protocol, for what reason would I NOT be able to pass it into a generic method?

Answer

A protocol does not conform to itself. You must make this generic:

func takeInAnObjectThatWeOnlyKnowItsProtocol<T: MyProtocol>(object: T)
{
    logTheThing(object)
}

To the next question: why doesn't a protocol conform to itself? Because it doesn't. Eventually it probably will, but it doesn't today.

That said, given this specific code, there's no reason to make this generic at all. Just pass the protocol and it'll do exactly what you want:

func logTheThing(theThing: MyProtocol) {
    theThing.getMeAString()
}

func takeInAnObjectThatWeOnlyKnowItsProtocol(object: MyProtocol) {
    logTheThing(object)
}

Passing object: MyProtocol means "any type that conforms to MyProtocol" and that matches in both places. Passing <T: MyProtocol>(object: T) means "a specific, concrete type that conforms to MyProtocol" and "any type that conforms to MyProtocol" is not a "specific, concrete type" and so fails (today; again, they'll probably fix that some day).

(Unrelated note: There is never a good reason to return String! in Swift. Just return String. And you'd get a better error message and fewer other little problems if you don't use T! and just use T in your logging call. Unless you're bridging to ObjC, there is almost never a reason to pass or return ! types. They only make sense in pure Swift for properties.)