Generic class with class-bound constraint cannot be parametrized by class-bound protocol

放肆的年华 提交于 2021-02-05 11:11:58

问题


I'd like to have a generic weak reference to an object and parametrize it by a protocol that is class-bound.

Here is the code example that does not work in my case:

protocol Protocol: class { // also written as `protocol Protocol: AnyObject {`
    func function()
}


final class A: Protocol {
    func function() {}
}

final class Weak<Type> where Type: AnyObject {
    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

let a = A()
let something = Weak<Protocol>(property: a) // error: 'Weak' requires that 'Protocol' be a class type

I get an error on last line: 'Weak' requires that 'Protocol' be a class type.

As Protocol will always be of class type (which is the same as AnyObject) shouldn't that be allowed by the compiler? Is it possible to resolve that issue with swift 4?

If not, is it a limitation that can be resolved in a future version of swift or is it something impossible that the type system can not allow to happen?

A not accepted solution is to use @objc to the protocol declaration as in:

@obc protocol Protocol: class {
    func function()
}

as this leads to limitations.


回答1:


You are saying:

final class Weak<Type> where Type: AnyObject {

So you yourself are requiring that Type be a class (because that is exactly what AnyObject means).

A protocol is not a class. It is a protocol.




回答2:


This is currently impossible because protocols do not conform to themselves. A protocol is not a type; it is a recipe for a type. As the simplest example of why this is hard, consider this situation:

protocol X {
    init()
}

func f<T: X>(type: T.Type) -> T {
    return type.init()
}

f(type: X.self)

This would create a value of type X, but would that be? This isn't allowed, and the mechanism that prevents it is that protocols do not conform to themselves. This crops up in a lots of other ways, and your example is one of them.

Some of this will be addressed when more of the Generics Manifesto is implemented, particularly the section Generalized Existentials, but currently what you want isn't possible. You can't have a "weak reference to some unknown thing." You need to know what that thing is.

The standard solution to this problem is a box, not a protocol. See Weak Arrays for an example.




回答3:


If I understand your problem I design this solution

First I created a protocol called WeakProtocol, this will be our base of protocols

protocol WeakProtocol : AnyObject {}

After change your protocol to extends this new WeakProtocol

protocol Protocol: WeakProtocol {
    func function()
}

after your Weak class you need apply a WeakProtocol type example

final class Weak<Type> where Type: WeakProtocol {
    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

Now to implement you don't need say it Protocol because the constructor receive the protocol.

Complete code is

protocol WeakProtocol : AnyObject {}

protocol Protocol: WeakProtocol {
    func function()
}

final class A: Protocol {
    func function() {}
}

final class Weak<Type> where Type: WeakProtocol {

    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

let a = A()
let something = Weak(property: a)

You can too only change Protocol to extends AnyObject and Weak class to Type:Protocol, like this

protocol Protocol: AnyObject {
    func function()
}

final class A: Protocol {
    func function() {}
}

final class Weak<Type> where Type: Protocol {

    final private weak var property: Type?

    init(property: Type) {
        self.property = property
    }
}

let a = A()
let something = Weak(property: a)


来源:https://stackoverflow.com/questions/51541353/generic-class-with-class-bound-constraint-cannot-be-parametrized-by-class-bound

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!