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
I have a similar issue, although with a bit more generics unfortunately
Here is my situation:
public protocol TabGeneric {
associatedtype Content: View
var name: String { get }
var image: Image? { get }
@ViewBuilder var content: () -> Content { get }
}
public final class TabBuilder {
public init() {}
func tabs(_ tabs: [any TabGeneric]) {
let t = tab(tabs[0])
}
// func tab(_ tab: some TabGeneric) -> Text {
func tab<T: TabGeneric>(_ tab: T) -> TabComponentView<T> {
TabComponentView(content: tab.content)
// Text("\(tab.name)")
}
}
public struct TabComponentView<T: TabGeneric>: View {
let tab: T
public var body: some View {
tab.content()
.tabItem {
tab.image
Text(tab.name)
}
.tag(tab.name)
}
}
The code fails in the tabs function, on the line: let t = tab(tabs[0])
I can't wrap my head around why it fails. I understand that this cannot work: