I'd like to declare generic protocols similar to the following:
protocol Factory {
func createWidget<T, TWidget>(_ t: T) -> TWidget
where TWidget: Widget, TWidget.T == T
}
protocol Widget {
associatedtype T
func get() -> T
}
I'm hoping that I can implement concrete variations of Factory returning their own concrete and opaque Widget with a hidden implementation.
Here's an example implementation that fails to build:
struct ConcreteFactory: Factory {
func createWidget<T, TWidget>(_ t: T) -> TWidget
where TWidget: Widget, TWidget.T == T {
// This line has an error…
return ConcreteWidget(widgetValue: t)
}
}
struct ConcreteWidget<T>: Widget {
let widgetValue: T
init(widgetValue: T) {
self.widgetValue = widgetValue
}
func get() -> T {
return widgetValue
}
}
However, this does not compile.
At the line indicated, Swift's compiler give the error "Cannot convert return expression of type 'ConcreteWidget' to return type 'TWidget'".
I also tried having ConcreteFactory return a ConcreteWidget, but then the error is that ConcreteFactory doesn't conform to Factory.
This can’t work. When you call your createWidget method you specify two types T and TWidget.
struct MyWidget: Widget {
func get() -> Int { ... }
}
let widget: MyWidget = factory.createWidget(12)
In this Example TWidget is MyWidget and T is Int. And this shows nicely why your approach can’t work. You cannot assign a ConcreteWidget<Int> to a variable of type MyWidget.
What you need is a type-eraser for your widgets. Currently you have to write that yourself, but in the future the compiler will hopefully generate them automatically when needed.
struct AnyWidget<T>: Widget {
private let _get: () -> T
init<Other: Widget>(_ other: Other) where Other.T == T {
_get = other.get
}
func get() -> T {
return _get()
}
}
This allows you to write your factory protocol and implementation:
protocol Factory {
func createWidget<T>(_ t: T) -> AnyWidget<T>
}
struct ConcreteFactory: Factory {
func createWidget<T>(_ t: T) -> AnyWidget<T> {
return AnyWidget(ConcreteWidget(widgetValue: t))
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With