1

How can I constrain parameters of a generic type to protocols that are constrained to AnyObject?

Note I do not want to constrain my parameter to AnyObject, I'd like to allow protocols as parameters to my generic type, but only protocols that are themselves constrained to AnyObject.

Example:

struct Gen<P>
    where P: AnyObject     //  #1
{}

protocol CP: AnyObject {}
protocol SP {}

class C: CP {}
struct S: SP {}

let gc = Gen<CP>()         //  #2
let gs = Gen<SP>()         //  #3

This gives an error for both #2 and #3: 'Gen' requires that 'CP'/'SP' be a class type.

If I remove line #1, then both #2 and #3 compile with no error.

What I'd like to achieve is that #2 should compile (because CP conforms to AnyObject), but #3 should not (because SP does not conform to AnyObject).

Is there any way to do that?


In reply to a comment about why I'd want this:

I tried implementing a WeakRef, so I can have arrays (or other collections) that do not retain their elements. I wanted that WeakRef to allow protocols as its parameter (not only concrete types), so I can write something like var observers: [MyObserverProtocol].

The only way I found so far (using type erasure) has the drawback that now if I have a protocol P and a conforming value type T, then the compiler now allows me to declare a WeakRef<P> variable, and wrap a T instance in it, but it won't work at runtime (only works with ref types). I'd like to get a compile error in this case. So I'd like WeakRef to only allow protocols that are class-constrained, as its parameter.

struct WeakRef<T> {
    var value: T? { get { return (value_ as? T) }}
    weak var value_: AnyObject?
    init(value: T? = nil) { value_ = value as AnyObject }
}

Now WeakRef<ProtocolThatValueTypesCanConformTo> or even WeakRef<ValueType> happily compiles, but of course won't work at runtime, weakRef.value will always be nil.

I'd like to get a compile error if T is a value type, or if it's a protocol that value types are allowed to conform to. In these cases WeakRef doesn't really make sense, it only makes sense for ref types, and protocols that only ref types can conform to.

To me it feels like a more generic problem, that's why I didn't mention the specific use case in the original question, but I may be wrong about this.

imre
  • 1,667
  • 1
  • 14
  • 28
  • 1
    Why do you want to do this? – Sweeper Feb 17 '20 at 13:40
  • I think there is another error hidden under this one - if you add `typealias AnyObject = D` (where `D` is a simple empty protocol) then the compiler starts complaining about `Protocol type 'CP' cannot conform to 'D' because only concrete types can conform to protocols` – pckill Feb 17 '20 at 13:56
  • @Sweeper Added info about why I'm interested in this to the question. – imre Feb 17 '20 at 14:17
  • 2
    The whole notion that you are going to resolve a generic by saying `Gen()` where CP is a protocol is incoherent. A protocol is not a type, it cannot be used to resolve a generic. You could resolve the generic with C, but not with CP. In a way this is a variant of https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself – matt Feb 17 '20 at 14:33
  • Does this answer your question? [How do I declare an array of weak references in Swift?](https://stackoverflow.com/questions/24127587/how-do-i-declare-an-array-of-weak-references-in-swift) – pckill Feb 19 '20 at 16:51
  • @pckill No, the implementations there have the `T: AnyObject` restriction, therefore cannot be used with protocols (as `WeakRef`). My WeakRef does allow that, but in a slightly type-unsafe way, that's what I wanted to improve, and that's when I ran into the issue I asked about. – imre Feb 25 '20 at 05:24

0 Answers0