2

This is my first question on StackOverflow so please go easy on me.

I've been struggling with getting Swift to invoke the appropriate generic overload.

Suppose I have the following protocol -

protocol MyProtocol { }

And I have the following generic methods -

func foo<T>() -> T

func foo<T: MyProtocol>() -> T

One would expect that invoking foo() with a return type of T conforming to MyProtocol would invoke the appropriate overload.

let bar: MyProtocol = foo()

The above code actually invokes the following function during runtime and Cmd + Click in the IDE navigates to the wrong overload as well.

func foo<T>() -> T

For some reason I cannot get this to work properly in Xcode 7.1.1.

Am I missing something completely fundamental here or is this another Swift quirk?

EDIT

Adding an example of this behavior in action as per matt's request.

protocol MyProtocol { }

class MyProtoClass : MyProtocol { }

class Bar {

    func foo<T>(value: T) {
        print("T is Generic")
    }

    func foo(value: MyProtocol) {
        print("T conforms to MyProtocol")
    }
}

class MyClass<T> {

    var value: T
    init(value: T) { self.value = value }
    var b = Bar()

    func print() {
        b.foo(value)
    }
}

MyClass<MyProtocol>(value: MyProtoClass()).print()
MyClass<String>(value: "").print()

Copying and pasting the above code into a Swift command line application and executing yields the following output.

T is Generic
T is Generic
Randy
  • 2,270
  • 1
  • 15
  • 24
  • 1
    Could you show the actual code used for testing, please? A good SO question should have everything necessary to copy and paste and run on one's own machine. – matt Dec 03 '15 at 02:21
  • Thanks for your suggestion, matt. I've added the code to reproduce this behavior. – Randy Dec 03 '15 at 02:51
  • Very cool, thanks for doing that. – matt Dec 03 '15 at 03:21

2 Answers2

1

I think the problem here is that protocols in generics (and generally in Swift) don't work the way you want them to. They are not acting as first-class types. I know that's rather vague... but look at it this way; if you eliminate the func foo<T>(value: T) version of foo, your code won't even compile. In other words, Swift isn't making a choice of foo and choosing wrong; it's saying that b.foo(a1.value) does not call func foo<T: MyProtocol>(value: T).

I have a vague feeling that this is related to my question here:

Protocol doesn't conform to itself?

Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Reading through your SO post and it seems like there is something missing in Swift. If I pass MyProtoClass() or "" directly to foo() then the appropriate overload is called. When the generic type is wrapped in another type the compiler doesn't recognize that the type conforms to MyProtocol. – Randy Dec 03 '15 at 03:30
  • I know my answer is not very good. If you come to a better formulation, you should answer your own question (and you can even accept your own answer, and I'll probably upvote it!). – matt Dec 03 '15 at 03:31
  • I appreciate the feedback, matt. At this point I feel as if Swift is really in it's infancy and the type system is a work in progress. If I can find a workaround to this then I would accept that as a feasible answer given that Swift simply isn't there yet. – Randy Dec 03 '15 at 03:34
0

Okay, I am going to answer my own question here.

After some investigation it seems that Swift wants you to implement an extension with a type constraint on the generic parameter.

extension MyClass where T : MyProtocol {
    func print() {
        b.foo(value)
    }
}

I know this doesn't really solve the problem but it was sufficient enough for me as a work around in my real world use case.

The above sample would wind up looking something like the following.

protocol MyProtocol { }

class MyProtoClass : MyProtocol { }

class Bar {

    func foo<T>(value: T) {
        print("T is Generic")
    }

    func foo(value: MyProtocol) {
        print("T conforms to MyProtocol")
    }
}

class MyClass<T> {

    var value: T
    init(value: T) { self.value = value }
    var b = Bar()

    func print() {
        b.foo(value)
    }
}

extension MyClass where T : MyProtocol {

    func print() {
        b.foo(value)
    }
}

MyClass<MyProtoClass>(value: MyProtoClass()).print()
MyClass<String>(value: "").print()
Randy
  • 2,270
  • 1
  • 15
  • 24