4

I'm having issues trying to constrain generic type requirements to just reference types. Here's some example code:

class WeakHolder<Element: AnyObject> {
    weak var element: Element?

    init(element: Element) {
        self.element = element
    }
}

protocol Animal: class { }

class Dog: Animal { }

let dog: Animal = Dog()
let holder = WeakHolder<Animal>(element: dog) // Error: Using "Animal" as a concrete type conforming to protocol 'AnyObject' is not supported.

If I change the generic requirement to <Element: class>, I get the error class constraint can only appear on protocol declarations.

Is this a limitation of generics? Marking a protocol as class is enough to have a weak reference to that protocol, is there no equivalent in generics?

Mark
  • 7,167
  • 4
  • 44
  • 68
  • Dog is not an AnyObject since Dog is a protocol – Zonily Jame Nov 30 '16 at 21:16
  • Based on my small experience this may be a limitation for generics as of swift 2.3. You may be able to fix this though by using classes instead of protocols – Zonily Jame Nov 30 '16 at 21:20
  • I need to use protocols in my implementation. – Mark Nov 30 '16 at 21:21
  • You're trying to create a class that's reusable for a huge part of your code aren't you? – Zonily Jame Nov 30 '16 at 21:22
  • I hope someone has a fix for this. – Zonily Jame Nov 30 '16 at 21:23
  • is doing this not acceptable? `class WeakHolder { weak var element: Element? init(element: Element) { self.element = element } }` – Zonily Jame Nov 30 '16 at 21:25
  • 1
    I'm using dependency injection in my project, so I work entirely with protocols. Yeah, I'm more looking for a way to constrain a generic to a reference type. – Mark Nov 30 '16 at 21:35
  • The problem is that [protocols don't conform to themselves](http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself) – you can nearly always solve this problem with a type erasure, such as Rob shows in his answer to the Q&A. In your case, you'd want to make an `AnyAnimal`. – Hamish Nov 30 '16 at 21:46

2 Answers2

0

The simple answer is that you cannot have a generic type that is a protocol.

Writing out the syntax makes it clear how this works: class/struct GenericType<TypeName: TypeConstraints> {}

let thing = GenericType<Type>() where Type is a class or struct that adheres to any constraints

A Protocol Requiring adopting Types to be a class means any adopters are classes, but the Protocol itself is still not a Type.

It's possible generics could at some point support protocols, but it would require changing the general approach to either protocols or generics. Though your specific example may be possible with a smaller amount of work behind the scenes, so it's possible this may be implemented at some point.

You can take a look at The Generics Manifesto if you want to see the direction they're going. Skimming it I didn't find anything directly related to your use case, but it's fairly specific so it may not be included in the parameters of the document.

GetSwifty
  • 7,568
  • 1
  • 29
  • 46
  • Ah, the generalized class constraint is exactly what I'm looking for. Good to know that it's being considered for a future version of Swift. – Mark Dec 01 '16 at 02:32
0

Another solution that worked in my particular case is the following:

class WeakHolder<Element: AnyObject> {
    weak var element: Element?

    init(element: Element) {
        self.element = element
    }
}

protocol Animal: class { }

class Dog: Animal { }

let dog: Animal = Dog()
let holder = WeakHolder<AnyObject>(element: dog as AnyObject)

When accessing element, I simply need to perform a downcast back to my protocol. Of course, I'll lose compile time safety when using this class with value types, but that's a non-issue in my situation.

Mark
  • 7,167
  • 4
  • 44
  • 68