Class that conforms to protocol with generic function returning associatedtype does not compile

Hi all,

I'm trying to conform a class to a simple protocol that has a generic function and returns an associatedtype. It looks like this:

import SwiftUI

protocol MyActualProtocol: AnyObject {
    associatedtype U: View
    func someView<T: View>(outterView: T) -> U
}
 
class NotWorkingImpl: MyActualProtocol {
    func someView<T: View>(outterView: T) -> some View {
        Color.red
        // a // returning 'outerView' doesn't compile either
    }
}

But the class doesn't compile with the error:
Type 'NotWorkingImpl' does not conform to protocol 'MyActualProtocol'
It seems like I have to specify the concrete returning type like this:

class WorkingImpl: MyActualProtocol {
    func someView<T: View>(outterView: T) -> Color {
        Color.red
    }
}

But I don't want to expose the type to the outside world.
Doing some tests I have found that removing the generic code it works:

protocol WorkingA: AnyObject {
    associatedtype U: View
    func someView() -> U
}

class WorkingAImpl: WorkingA {
    func someView() -> some View {
        Color.red
    }
}

But now I don't have the outterView anymore and I need it.
Also if I remove the associatedtype from the protocol and the function is still generic, but returning a concrete type, it works:

protocol WorkingB: AnyObject {
    func colorView<T: View>(outterView: T) -> Color
}

class WorkingBImpl: WorkingB {
    func colorView<T: View>(outterView: T) -> Color {
        Color.red
    }
}

But again I don't want to expose the concrete type.

Looking at the code in WorkingAImpl class and in NotWorkingImpl the only difference is the generic code but the implementation of the function is the same, so from my understanding it should work.
So my questions is:

  • Is it a bug or am I doing something wrong? Can it be done? In another way?

Thanks!

1 Like

I forgot to say that if I remove the protocol conformance then the class compiles:

class NotWorkingImpl {
    func someView<T: View>(outterView: T) -> some View {
        Color.red
        // a // returning param 'a' also compiles
    }
}

It works (despite the class name) but I need the protocol to pass the object around.

So its seems a bug to me.

You are correct. The compiler knows the exact type of the view returned by someView, without any ambiguity. But it does not use it to automatically infer the associated type U. It could do it, as it does when you explicitly specify the type instead of some View, and it would be very convenient. It looks like such inference is just not implemented yet for opaque some types.

I suggest opening an issue on https://bugs.swift.org/

1 Like

I looks like generics are required for the issue to show up:

// A protocol with an associated type
protocol PAT {
    associatedtype U
    func f() -> U
}

// βœ… Compiles
struct S1: PAT {
    func f() -> String  { "Hello" }
}

// βœ… Compiles
struct S2: PAT {
    func f() -> some StringProtocol { "Hello" }
}

// ===========

// A protocol with a constrained associated type
protocol ConstrainedPAT {
    associatedtype U: StringProtocol
    func f() -> U
}

// βœ… Compiles
struct S3: ConstrainedPAT {
    func f() -> String  { "Hello" }
}

// βœ… Compiles
struct S4: ConstrainedPAT {
    func f() -> some StringProtocol { "Hello" }
}

// ===========

// A protocol with an associated type and a generic requirement
protocol GenericPAT {
    associatedtype U
    func f<T>(t: T) -> U
}

// βœ… Compiles
struct S5: GenericPAT {
    func f<T>(t: T) -> String  { "Hello" }
}

// ❌ error: type 'S6' does not conform to protocol 'GenericPAT'
// struct S6: GenericPAT {
//       ^
// note: protocol requires nested type 'U'; do you want to add it?
//     associatedtype U
//                    ^
struct S6: GenericPAT {
    func f<T>(t: T) -> some StringProtocol { "Hello" }
}

OK, I've opened an issue:

2 Likes