Atreya Ranganath
Atreya Ranganath

Reputation: 116

Type constraints on contained generic type parameter

I expected the following code to print "extension" in both cases. But the type constraint on the extension does not take effect on the contained generic type. I see the same behavior when constraining on protocols too.

class Generic1<T1> {
    func doSomething() {
        print("base")
    }
}

extension Generic1 where T1 == String {
    func doSomething() {
        print("extension")
    }
}

class Generic2<T2> {
    private let generic1 = Generic1<T2>()
    func doSomething() {
        generic1.doSomething()
    }
}

Generic1<String>().doSomething()    // prints extension
Generic2<String>().doSomething()    // prints base

The only workaround I currently have is to constrain the outer generic as well like so:

extension Generic2 where T2 == String {
    func doSomething() {
        generic1.doSomething()
    }
}

Why does this happen? Are there better solutions?

Edit: Just for completeness, the workaround that suited my case was the following:

class Generic1<T1> {
    func doSomething() {
        print("base")
    }
}

class StringGeneric1: Generic1<String> {
    override func doSomething() {
        print("extension")
    }
}

class Generic2<T2> {
    private let generic1: Generic1<T2>

    init (_ generic1: Generic1<T2>) {
        self.generic1 = generic1
    }

    func doSomething() {
        generic1.doSomething()
    }
}

Generic1<String>().doSomething()    // prints "base"
Generic2<String>(StringGeneric1()).doSomething()    // prints "extension"

Upvotes: 1

Views: 411

Answers (2)

Charles Srstka
Charles Srstka

Reputation: 17060

The problem is that methods defined in extensions are statically dispatched. So when you have:

class Generic2<T2> {
    private let generic1 = Generic1<T2>()
    func doSomething() {
        generic1.doSomething()
    }
}

The compiler cannot know here whether T2 is going to be a String or not, so it generates a call to the method in the base class. When you explicitly specify that T2 is String, then with that information, the compiler can generate a call to the extension's method here. Otherwise, though, the type of T2 isn't known until runtime, so you can't reach the extension method via static dispatch.

Upvotes: 1

dktaylor
dktaylor

Reputation: 914

This may be solved when conditional conformances are added with Swift 4.2

Upvotes: 0

Related Questions