d.ennis
d.ennis

Reputation: 3455

Extension with a generic where clause is not beeing called

The following code works just as expected:

protocol Storeable {
    associatedtype Value
    func create(value: Value)
}

struct DefaultStore<V>: Storeable {
    typealias Value = V
    func create(value: V) {
        print("base impl. is called with value type: \(String(describing: V.self))")
    }
}

class Event {
    var title: String = ""
    init(title: String) {
        self.title = title
    }
}

extension DefaultStore where Value: Event {
    func create(value: V) {
        print("event impl. is called with event: \(value.title)")
    }
}

DefaultStore().create(value: Event(title: "Dance Party")) 
// prints "event impl. is called with event: Dance Party"
DefaultStore().create(value: "a string object") 
// prints "base impl. is called with value of type: String"

Basically I call a function on the generic class DefaultStore and the compiler calls the correct implementation depending on the underlying type.

Now I have a scenario where I want the exact same thing except, that it is wrapped in another object called Wrapper:

struct Wrapper<StoreType: Storeable,ValueType> where StoreType.Value == ValueType { 
    let store: StoreType
    func create(value: ValueType) { 
        store.create(value: value)
    }
}

let defaultStore = DefaultStore<Event>()
let wrapper = Wrapper(store: defaultStore)
wrapper.create(value: Event(title: "Düsseldorfer Symphoniker"))
// prints: "base impl. is called with value of type: Event"

Here I expect that the extension for Event is called but instead the base implementation is called. Anyone any idea what I am doing wrong?

--

Update:

@matt was guessing that there might be a compile time issue: "without the wrapper when you say DefaultStore().create... the Event info can be read all the way back to the DefaultStore generic parameterized type. But in your wrapper the DefaultStore is created first so it is the general type and that's all it is."

I believe that is not the case because you can also create the DefaultsStore first and it's still working without the Wrapper. Also the compiler warns me if the types don't match.

Upvotes: 1

Views: 196

Answers (1)

matt
matt

Reputation: 535249

Nothing to do with "wrappers".

You seem to think that a where extension on a generic struct is a kind of substitute for some sort of dynamic dispatch, i.e. we're going to wait until runtime, look to see what kind of object the parameter to create really is, and dispatch accordingly. That's not how it works. Everything has to be resolved at compile time. At compile time, what we know is that you are not calling create with an Event — you are calling it with a ValueType. So it passes into the create with generic V.

So, for example:

struct Wrapper {
    let store: DefaultStore<Event>
    func create(value: Event) {
        store.create(value: value)
    }
}

That works the way you have in mind because we are exposing the fact that this is an Event as far as the DefaultStore is concerned at the point of calling create.


That's just a guess at what your confusion is.

Another possibility is you might imagine that the generic resolution of ValueType somehow "leaks" up to the resolution of V, but that's not true either.

Yet another possibility is you might suppose that a DefaultStore<Event> is a subtype of DefaultStore<Storable> or similar, as if there were dynamic dispatch for parameterized types, but that is exactly what is not the case. Everything has to be known completely at compile time at the call site.

Upvotes: 1

Related Questions