protocol Letter {
init()
}
struct A: Letter {}
struct LetterContainer<L: Letter> {
var letter: L
}
func initializeLetterContainer<L: Letter>(_ letter: L){
let result = LetterContainer<L>(letter: letter)
print(result)
}
// add a return type that includes L and it fails
func initializeAndReturnLetterContainer<L: Letter>(_ letter: L) -> LetterContainer<L> {
let result = LetterContainer<L>(letter: letter)
print(result)
return result
}
func getLetterType() -> Letter.Type {
return A.self
}
let letterType = getLetterType()
let letter = letterType.init()
initializeLetterContainer(letter) // works
// _ = initializeAndReturnLetterContainer(letter) // Fails to compile: Type 'any Letter' cannot conform to 'Letter'
why does the generic function initializeLetterContainer consume the returned result of getLetterType() just fine while a similar function, initializeAndReturnLetterContainer fails to compile?
When T or a T-rooted associated type appears in a non-covariant position in the result type, T cannot be bound to the underlying type of an existential value because there would be no way to represent the type-erased result. This is essentially the same property as descibed for the parameter types that prevents opening of existentials, as described above. For example:
In your example, L appears in non-covariant position in the function's return type (generic parameters in Swift aren't covariant, with a few hardcoded exceptions such as Array), so this limitation applies.
I tried doing that but it fails to compile (I think for the same reason as the original version of code):
protocol Letter {
init()
}
struct A: Letter {}
struct LetterContainer<L: Letter> {
var letter: L
}
func initializeLetterContainer<L: Letter>(_ letter: L){
let result = LetterContainer<L>(letter: letter)
print(result)
}
// add a return type that includes L and it fails
func initializeAndReturnLetterContainer<L: Letter>(_ letter: L) -> LetterContainer<L> {
let result = LetterContainer<L>(letter: letter)
print(result)
return result
}
func getLetterType() -> Letter.Type {
return A.self
}
extension Letter {
func initializeAndReturnLetterContainerFromSelf() -> LetterContainer<Self> {
return initializeAndReturnLetterContainer(self)
}
}
let letterType = getLetterType()
let letter = letterType.init()
initializeLetterContainer(letter) // works
// _ = initializeAndReturnLetterContainer(letter) // Fails to compile: Type 'any Letter' cannot conform to 'Letter'
// _ = letter.initializeAndReturnLetterContainerFromSelf() // Fails to compile: Member 'initializeAndReturnLetterContainerFromSelf' cannot be used on value of type 'any Letter'; consider using a generic constraint instead