protocol Builder {
associatedtype Output where Output: MyProtocol
func build() -> Output?
}
// concrete type
struct ABuilder: Builder {
func builder() -> MyProtocol {
if someCondition {
return aSubClassOfMyProtocol
} else {
return anotherSubClassOfMyProtocol
}
}
}
MyProtocol
is a protocol type. It is also the Output
constraint. Because the concrete Builder ABuilder
is going to return two different sub class types that conform MyProtocol
. How can I make the generic constraint work?
I am trying to make the generic constraint be the same.
CodePudding user response:
You can make build()
function generic or use typecasting. Anyway this two ways determine concrete type outside of method not inside.
So first of all you need to remove associatedtype
. If you specify associated type Output
it means that the implementation of your Builder
protocol should return some concrete type which conforms to MyProtocol
. If you want your Builder
to return any type which conforms to MyProtocol
not the concrete one specified for this implementation of Builder then you shouldn't declare any associated types.
Let's see how type casting can work:
protocol Builder {
func build() -> MyProtocol?
}
struct ABuilder: Builder {
func build() -> MyProtocol? {
// some code
}
}
let builder = ABuilder()
guard let a = builder.build() as? SomeClass else { return }
Or we can use generic approach:
protocol Builder {
func build<T: MyProtocol>() -> T?
}
struct ABuilder: Builder {
func build<T: MyProtocol>() -> T? {
// some code
}
}
let builder = ABuilder()
let a: SomeClass = builder.build()
You can read more about that here, here and here. Moreover I strongly recommend watching this video which describes all the limitations of generics in swift.
CodePudding user response:
I think you are correct that having the typecasting outside the Builder
is a better way. I also found another possibility. The difference is that we have the concrete class types from the build
function.
protocol MyProtocol { }
protocol Builder {
associatedtype Output
func build() -> Output?
}
struct ASubClassOfMyProtocol: MyProtocol {}
struct AnotherSubClassOfMyProtocol: MyProtocol {}
struct ABuilder: Builder {
func build() -> (any MyProtocol)? {
if Int.random(in: 0...10) < 5 {
return ASubClassOfMyProtocol()
} else {
return AnotherSubClassOfMyProtocol()
}
}
}