Can't call extension method on generic Array class when element type is a Protocol [Swift]

折月煮酒 提交于 2021-02-05 11:21:07

问题


The following fails as per the error message quoted in the comment. It has been boiled down to the bare minimum, so the code below has no apparent practical value. I'm just trying to get a handle on the truly bizarre (in my opinion) error message. The reason I want to declare the array as [P] and not [S] is for the usual run-time polymorphism of the array contents.

protocol P {
    func sp()
}

struct S: P {
    func sp() {}
}

extension Array where Element: P {
    func am() {}
}

func t() {
    let goodA = [S]()
    goodA.am() // No problem

    let badA = [P]()
    badA.am() // Error: '[P]' is not convertible to 'P'
}

回答1:


Protocol declarations define new independent types. While they do behave differently than classes, enums, and structs, they can act as the type for a variable or property (provided there are no associated type constraints, but hold on). For instance

protocol MyProtocol {}
let myConst: MyProtocol // myConst has type `MyProtocol`

In a protocol extension with a generic where clause, there are two different ways to constrain the generic type in question: either with :_ or ==. There is a difference between the two.

Use of :_

The colon specifies that the generic type is something matching the type or protocol specified. It can be read as 'inherits from' or 'conforms to'. For example, anything specified in the extensions apply only when the associated type conforms to Equatable or inherits from SomeClass

protocol AnotherProtocol {
    associatedtype MyType
}

class SomeClass {}

extension AnotherProtocol where MyType: SomeClass {}
extension AnotherProtocol where MyType: Equatable {}

Use of ==

The == operator in a generic where clause specifies that the generic type is exactly the type specified and no other. The code below gives a compile-time error

class CustomType {}
class CustomTypeII: CustomType {}
class MyClass: AnotherProtocol { typealias MyType = CustomTypeII }

extension AnotherProtocol where MyType == CustomType {
    func myFunc() {}
}

let instance = MyClass()
instance.myFunc() // Error: `CustomTypeII` is not the same type as `CustomType`

The == operator cannot be used to constrain a generic type to a protocol with associated types. The compiler will complain that the protocol has an associated type requirement.

protocol ThirdProtocol {}

extension ThirdProtocol where Self == AnotherProtocol {} // Error: `AnotherProtocol` has associated type requirements

In essence, the specification is ambiguous: there is no single AnotherProtocol type, much in the same way that there is no single Array type. The associated type in a protocol acts similarly to the generic arguments of a generic type. Here, the use of a colon is needed to specify that the generic type 'is some' AnotherProtocol type (opaque types follow from the need to specify a specific 'type' of a protocol with associated type constraints, but that is beyond the question).

Your extensions

The extension of Array applies only to array instances whose elements are some type that conforms to the protocol type P, which does not apply to arrays whose elements are the type P. Protocols not conforming to themselves is akin to classes not inheriting from themselves.

This is an old thread, but hopefully this helped.



来源:https://stackoverflow.com/questions/32741043/cant-call-extension-method-on-generic-array-class-when-element-type-is-a-protoc

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