Type 'any Protocol' cannot conform to 'Protocol'

I have the following problem:

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?

2 Likes

I think you're running into this limitation of SE-0352: Implicitly Opened Existentials:

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:

func cannotOpen7<T: P>(_ value: T) -> X<T> { /*...*/ }

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.

3 Likes

OK. Thank you.

This may help: Protocol as a type cannot conform to the protocol itself - #3 by jjrscott

1 Like

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

It works if you remove the generic from LetterContainer:

struct LetterContainer {
    var letter: Letter
}

Is that acceptable given you're not using it due to getLetterType()?

I guess I'm wondering what you would expect ???? to be here given the concrete type is hidden inside getLetterType():

let letterContainer: LetterContainer<????> =
  initializeAndReturnLetterContainer(getLetterType().init())

Yes, that would be an acceptable work-around.

1 Like

One would expect a container with something that conforms to the Letter protocol.

I have a similar issue, although with a bit more generics unfortunately :sweat_smile:

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:

func cannotOpen7<T: P>(_ value: T) -> X<T> { /*...*/ }

because of what @ole said:

generic parameters in Swift aren't covariant, with a few hardcoded exceptions such as Array

But is there a way around this somehow?

My final goal is to take [any TabGeneric] and create a TabView out of it, ie:

func tabView(_ tabs: [any TabGeneric]) -> some View {
    TabView {
        ForEach(tabs.indices, id: \.self) { tabIndex in
            tab(tabs[tabIndex])
        }
    }
}