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?

1 Like

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.

2 Likes

OK. Thank you.

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

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.