프로토콜이 자체 준수하지 않습니까?


125

이 Swift 코드는 왜 컴파일되지 않습니까?

protocol P { }
struct S: P { }

let arr:[P] = [ S() ]

extension Array where Element : P {
    func test<T>() -> [T] {
        return []
    }
}

let result : [S] = arr.test()

컴파일러는 "유형 P이 프로토콜을 따르지 않습니다 "라고 말합니다 P(또는 이후 버전의 Swift에서는 " 'P'를 준수하는 구체적인 유형으로 'P'를 사용하는 것은 지원되지 않습니다").

왜 안돼? 이것은 어쨌든 언어의 구멍처럼 느껴집니다. 문제 arr는 배열 을 프로토콜 유형 의 배열 로 선언하는 데 기인한다고 생각하지만 부당한 일입니까? 형식 계층 구조와 같은 구조체를 제공하는 데 도움이되는 프로토콜이 정확히 있다고 생각 했습니까?


1
let arr줄 에서 형식 주석을 제거하면 컴파일러에서 형식을 유추 [S]하고 코드를 컴파일합니다. 프로토콜 유형은 클래스-슈퍼 클래스 관계와 같은 방식으로 사용할 수없는 것 같습니다.
vadian

1
@vadian 맞습니다. "문제가 배열을 arr을 프로토콜 유형의 배열로 선언함으로써 발생한다는 것을 알았습니다"라고 말할 때 내 질문에서 언급 한 것입니다. 그러나 내 질문에 계속 말하면 프로토콜의 요점은 대개 클래스-수퍼 클래스 관계와 같은 방식으로 사용할 있다는 것입니다 ! 그들은되는 구성 구조체의 세계에 계층 구조의 종류를 제공 할 수 있습니다. 그리고 그들은 보통합니다. 문제는 왜 여기서 작동하지 않아야 합니까?
matt

1
여전히 Xcode 7.1에서 작동하지 않지만 오류 메시지는 " 'P'프로토콜을 따르는 구체적인 유형으로 'P'를 사용하는 것은 지원되지 않습니다 . '
Martin R

1
@MartinR 더 나은 오류 메시지입니다. 그러나 그것은 여전히 ​​언어의 구멍처럼 느껴집니다.
matt

확실한! 로도 protocol P : Q { }P는 Q 와 일치하지 않습니다.
Martin R

답변:


66

편집 : 새로운 진단을 제공하는 또 다른 주요 릴리스 인 Swift를 사용하여 18 개월 동안 작업하고 @AyBayBay의 의견 으로이 답변을 다시 작성하고 싶습니다. 새로운 진단은 다음과 같습니다.

" 'P'프로토콜을 따르는 구체적인 유형으로 'P'를 사용하는 것은 지원되지 않습니다."

실제로이 모든 것이 훨씬 더 명확 해집니다. 이 확장은 :

extension Array where Element : P {

Element == P이후 P의 구체적인 적합성으로 간주 되지 않는 경우에는 적용되지 않습니다 P. 아래의 "상자에 넣는"솔루션이 여전히 가장 일반적인 솔루션입니다.


기존 답변 :

또 다른 메타 타입 사례입니다. Swift는 실제로 사소하지 않은 것들에 대해 구체적인 유형에 도달하기를 원합니다. [P]구체적인 유형이 아닙니다 (에 알려진 크기의 메모리 블록을 할당 할 수 없음 P). (실제로는 사실이라고 생각하지 않습니다 . 간접적으로 수행P 되기 때문에 절대적으로 크기가 큰 것을 만들 수 있습니다 .) 이것이 "하지 않아야"하는 경우에 대한 증거는 없다고 생각합니다. 이것은 "아직 작동하지 않는"사례 중 하나와 매우 비슷합니다. (불행히도 Apple이 이러한 경우의 차이점을 확인하도록하는 것은 거의 불가능합니다.) 변수 유형일 수있는 사실Array<P>Array)는 이미이 방향으로 일부 작업을 수행했음을 나타내지 만 Swift 메타 타입에는 날카로운 모서리와 구현되지 않은 사례가 많이 있습니다. 나는 당신이 그것보다 더 나은 "왜"대답을 얻을 것이라고 생각하지 않습니다. "컴파일러가 허용하지 않기 때문에." (불만족, 알아요. 내 스위프트의 삶 전체…)

해결책은 거의 항상 물건을 상자에 넣는 것입니다. 타입 지우개를 만듭니다.

protocol P { }
struct S: P { }

struct AnyPArray {
    var array: [P]
    init(_ array:[P]) { self.array = array }
}

extension AnyPArray {
    func test<T>() -> [T] {
        return []
    }
}

let arr = AnyPArray([S()])
let result: [S] = arr.test()

Swift가 직접 (직접 기대하는) 작업을 수행 할 수있게하면 자동 으로이 상자를 만드는 것입니다. 재귀 열거 형은이 역사를 정확히 가지고있었습니다. 당신은 그것들을 박스에 넣어야했고 엄청나게 짜증나고 제한적이었습니다. 그리고 마침내 컴파일러 indirect는 같은 일을 더 자동적으로하도록 추가 했습니다.


이 답변에 많은 유용한 정보가 있지만 Tomohiro의 답변에 실제 솔루션이 여기에 제시된 권투 솔루션보다 낫습니다.
jsadler

@jsadler 문제는 제한을 해결하는 방법이 아니라 제한이 존재하는 이유입니다. 실제로, 설명이 진행되는 한, Tomohiro의 해결책은 답변보다 많은 질문을 제기합니다. ==배열 예제에서 사용 하면 오류가 발생합니다. 동일한 유형 요구 사항은 일반 매개 변수 'Element'를 제네릭이 아닙니다. "Tomohiro가 ==동일한 오류 를 생성 하지 않는 이유는 무엇 입니까?
matt

@Rob Napier 나는 아직도 당신의 응답에 당황합니다. Swift는 솔루션과 오리지널 솔루션 중 더 구체적인 점을 어떻게 보십니까? 당신은 방금 구조체에 물건을 포장 한 것 같았습니다 ... Idk 어쩌면 나는 스위프트 타입 시스템을 이해하기 위해 고군분투하고 있지만 이것은 마술 부두처럼 보입니다.
AyBayBay

@AyBayBay 업데이트 된 답변입니다.
Rob Napier

@RobNapier에게 대단히 감사합니다. 저는 항상 귀하의 답변 속도와 놀랍도록 솔직하게 사람들을 도울 시간을 찾는 방법에 놀랐습니다. 그럼에도 불구하고 새로운 편집 내용은 분명히 원근법에 있습니다. 유형 지적을 이해하는 것도 도움이되었습니다. 특히이 문서에서는 환상적인 일을했다 : krakendev.io/blog/generic-protocols-and-their-shortcomings은 TBH 내가이 물건의 일부에 대해 어떻게 생각하는지 나도 몰라. 우리는 언어의 허점을 설명하는 것처럼 보이지만 Idk가 어떻게 애플이 이것을 구현할 것인지에 대해 Idk
AyBayBay

109

프로토콜이 왜 자신과 맞지 않습니까?

일반적인 경우에 프로토콜이 자신을 준수하도록 허용하는 것은 좋지 않습니다. 문제는 정적 프로토콜 요구 사항에 있습니다.

여기에는 다음이 포함됩니다.

  • static 방법과 속성
  • 이니셜 라이저
  • 연관된 유형 (현재는 실제 유형으로 프로토콜 사용을 방해하지만)

우리는 일반적인 자리 표시 자 T에서 이러한 요구 사항에 액세스 할 수 있습니다. T : P그러나 앞으로 구체적인 형식이 없으므로 프로토콜 유형 자체에서는 액세스 할 수 없습니다 . 그러므로 우리는 허용 할 수 없습니다 TP.

Array확장을 적용 할 수 있도록 허용 한 경우 다음 예제에서 어떤 일이 발생하는지 고려하십시오 [P].

protocol P {
  init()
}

struct S  : P {}
struct S1 : P {}

extension Array where Element : P {
  mutating func appendNew() {
    // If Element is P, we cannot possibly construct a new instance of it, as you cannot
    // construct an instance of a protocol.
    append(Element())
  }
}

var arr: [P] = [S(), S1()]

// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported
arr.appendNew()

우리는 가능하게 호출 할 수 없습니다 appendNew()[P]있기 때문에, P((가) Element) 구체적인 유형 아니므로 인스턴스화 할 수 없습니다. 그것은 해야한다 콘크리트 형식의 요소 유형의 요건에 적합와 배열에 호출 P.

정적 메서드 및 속성 요구 사항과 비슷한 이야기입니다.

protocol P {
  static func foo()
  static var bar: Int { get }
}

struct SomeGeneric<T : P> {

  func baz() {
    // If T is P, what's the value of bar? There isn't one – because there's no
    // implementation of bar's getter defined on P itself.
    print(T.bar)

    T.foo() // If T is P, what method are we calling here?
  }
}

// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported
SomeGeneric<P>().baz()

의 관점에서 이야기 할 수 없습니다 SomeGeneric<P>. 우리는 (이없는 방법 통지 정적 프로토콜 요구 사항의 구체적인 구현해야 할 의 구현 foo()또는 bar위의 예에서 정의 된). P확장 에서 이러한 요구 사항의 구현을 정의 할 수 있지만 이러한 요구 사항 은 구체적인 유형에 대해서만 정의됩니다. P여전히 P자체 호출 할 수는 없습니다 .

이 때문에 Swift는 프로토콜을 자체 요구 사항에 맞는 유형으로 사용하지 못하도록합니다. 프로토콜에 정적 요구 사항이있을 때는 그렇지 않기 때문입니다.

인스턴스 프로토콜 요구 사항은 프로토콜에 맞는 실제 인스턴스에서 호출 해야 하므로 문제가되지 않으므로 요구 사항을 구현해야합니다. 따라서로 유형이 지정된 인스턴스에서 요구 사항을 호출하면 해당 요구 사항 P의 기본 구체 유형 구현으로 해당 호출을 전달할 수 있습니다.

그러나이 경우 규칙에 특별한 예외를두면 프로토콜이 일반 코드로 처리되는 방식에 놀라운 불일치가 발생할 수 있습니다. 그러나 현재 상황은 associatedtype요구 사항과 크게 다르지 않습니다. 이는 현재 프로토콜을 유형으로 사용하지 못하게합니다. 정적 요구 사항이있을 때 프로토콜 자체를 따르는 유형으로 프로토콜을 사용하지 못하게하는 제한이 있으면 향후 버전의 언어에 대한 옵션이 될 수 있습니다.

편집 : 그리고 아래에서 살펴본 것처럼 이것은 스위프트 팀이 목표로하는 것과 같습니다.


@objc 프로토콜

사실 실제로 언어가 프로토콜을 처리하는 방식과 정확히 같습니다@objc . 정적 요구 사항이 없으면 자체적으로 준수합니다.

다음은 잘 컴파일됩니다.

import Foundation

@objc protocol P {
  func foo()
}

class C : P {
  func foo() {
    print("C's foo called!")
  }
}

func baz<T : P>(_ t: T) {
  t.foo()
}

let c: P = C()
baz(c)

baz그 요구 T에 부합를 P; 그러나 우리는에서 대체 할 수 있습니다 P에 대한 T때문에 P정적 요구 사항이 없습니다. 에 정적 요구 사항을 추가 P하면 예제가 더 이상 컴파일되지 않습니다.

import Foundation

@objc protocol P {
  static func bar()
  func foo()
}

class C : P {

  static func bar() {
    print("C's bar called")
  }

  func foo() {
    print("C's foo called!")
  }
}

func baz<T : P>(_ t: T) {
  t.foo()
}

let c: P = C()
baz(c) // error: Cannot invoke 'baz' with an argument list of type '(P)'

따라서이 문제에 대한 한 가지 해결 방법은 프로토콜을 만드는 것입니다 @objc. 물론 이것은 Obj-C 런타임을 요구할뿐만 아니라 적합한 유형을 클래스로 강제해야하므로 Linux와 같은 Apple 이외의 플랫폼에서는 실행 가능하지 않기 때문에 많은 경우에 이상적인 해결 방법은 아닙니다.

그러나이 제한이 언어가 프로토콜에 대해 '정적 요구 사항없이 프로토콜을 자체적으로 준수'하는 이미 구현 한 주된 이유 중 하나라고 생각 @objc합니다. 컴파일러에 의해 작성된 일반적인 코드는 크게 단순화 될 수 있습니다.

왜? 때문에 @objc프로토콜 형식의 값은 그 요구 사항을 사용하여 파견 효과적으로 단지 클래스 참조입니다 objc_msgSend. 반대로 @objc프로토콜이 아닌 유형의 값은 (잠재적으로 저장된) 랩핑 된 값의 메모리를 관리하고 다른 구현을 호출 할 구현을 결정하기 위해 값과 감시 테이블을 모두 가지고 있기 때문에 더 복잡합니다. 요구 사항.

때문에이 단순화 표현의 @objc프로토콜 같은 프로토콜 유형의 값은 P유형의 '일반적인 값'몇 가지 일반적인 자리와 같은 메모리 표현을 공유 할 수 있습니다 T : P, 아마도 자기 적합성을 허용하도록 스위프트 팀이 쉽게 그것을 만드는. 비- @objc프로토콜에 대해서도 마찬가지 이지만, 이러한 일반 값에는 현재 값 또는 프로토콜 감시 테이블이 없습니다.

그러나이 기능 의도적이며 @objcSwift 팀 구성원 Slava Pestov 가 SR-55 에 대한 귀하의 질문에 대한 답변 ( 이 질문에 의해 표시됨) 의해 확인 된 것처럼 비 프로토콜 로 롤아웃되기를 바랍니다 .

Matt Neuburg 님이 댓글을 추가했습니다-2017 년 9 월 7 일 오후 1시 33 분

이것은 컴파일합니다 :

@objc protocol P {}
class C: P {}

func process<T: P>(item: T) -> T { return item }
func f(image: P) { let processed: P = process(item:image) }

추가 @objc하면 컴파일됩니다. 제거하면 다시 컴파일되지 않습니다. Stack Overflow를 통해 우리 중 일부는이 놀라운 것을 발견하고 그것이 의도적인지 또는 버그가있는 경우인지 알고 싶습니다.

Slava Pestov는 코멘트를 추가했습니다-2017 년 9 월 7 일 오후 1시 53 분

고의적으로이 제한을 해제하는 것이이 버그에 관한 것입니다. 내가 말했듯이 그것은 까다 롭고 아직 구체적인 계획이 없습니다.

언젠가 언젠가는 언어가 비 @objc프로토콜에 대해서도 지원할 수 있기를 바랍니다.

그러나 비 @objc프로토콜에 대한 현재 솔루션은 무엇 입니까?


프로토콜 제약 조건으로 확장 구현

Swift 3.1에서, 주어진 일반 플레이스 홀더 또는 관련 타입이 주어진 프로토콜 타입이어야한다는 제약 조건을 가진 확장을 원한다면 (그 프로토콜에 맞는 콘크리트 타입이 아니라) – ==제약 조건으로 간단히 정의 할 수 있습니다 .

예를 들어 배열 확장을 다음과 같이 작성할 수 있습니다.

extension Array where Element == P {
  func test<T>() -> [T] {
    return []
  }
}

let arr: [P] = [S()]
let result: [S] = arr.test()

물론 이것은 이제 우리가에 맞는 콘크리트 타입 요소를 가진 배열에서 그것을 호출하는 것을 막습니다 P. when에 대한 추가 확장을 정의하고 확장으로 Element : P넘어 가면 이를 해결할 수 == P있습니다.

extension Array where Element : P {
  func test<T>() -> [T] {
    return (self as [P]).test()
  }
}

let arr = [S()]
let result: [S] = arr.test()

그러나 이것은 [P]각 요소가 존재하는 컨테이너에 박스되어야하기 때문에 배열을 a로 변환하는 O (n) 변환을 수행한다는 점은 주목할 가치가 있습니다. 성능에 문제가있는 경우 확장 방법을 다시 구현하여 간단히 해결할 수 있습니다. 이것은 완전히 만족스러운 해결책 은 아닙니다 . 향후 언어 버전에 '프로토콜 유형 또는 프로토콜 유형에 따른 것'제약 조건 을 표현하는 방법이 포함되기를 바랍니다 .

Swift 3.1 이전에는 Rob그의 답변에서 알 수 있듯이 이것을 달성하는 가장 일반적인 방법은 단순히에 대한 래퍼 유형을 작성하는 [P]것입니다. 그런 다음 확장 메소드를 정의 할 수 있습니다.


프로토콜 형식의 인스턴스를 제한된 일반 자리 표시 자에 전달

다음과 같은 상황을 고려하십시오.

protocol P {
  var bar: Int { get set }
  func foo(str: String)
}

struct S : P {
  var bar: Int
  func foo(str: String) {/* ... */}
}

func takesConcreteP<T : P>(_ t: T) {/* ... */}

let p: P = S(bar: 5)

// error: Cannot invoke 'takesConcreteP' with an argument list of type '(P)'
takesConcreteP(p)

우리는 전달할 수 없습니다 ptakesConcreteP(_:)우리가 현재 대체 할 수 없기 때문에, P일반적인 자리를 위해 T : P. 이 문제를 해결할 수있는 몇 가지 방법을 살펴 보겠습니다.

1. 실존 존재

P대한 대체 를 시도하는 대신 T : P유형이 P지정된 래핑 된 콘크리트 유형을 파고 대체 할 수 있다면 어떨까요? 안타깝게도이 옵션은 현재 사용자가 직접 사용할 수없는 opening existentials 라는 언어 기능이 필요 합니다.

그러나 Swift 멤버에 액세스 할 때 암시 적으로 존재 항목 (프로토콜 유형 값)을 엽니 다 (즉, 런타임 유형을 발굴하여 일반 플레이스 홀더 형식으로 액세스 가능하게 함). 다음의 프로토콜 확장에서이 사실을 활용할 수 있습니다 P.

extension P {
  func callTakesConcreteP/*<Self : P>*/(/*self: Self*/) {
    takesConcreteP(self)
  }
}

Self확장 메서드가 사용 하는 암시 적 일반 자리 표시 자에 주의하십시오 . 이는 암시 적 self매개 변수 를 입력하는 데 사용됩니다. 이는 모든 프로토콜 확장 멤버가있는 장면에서 발생합니다. 프로토콜 유형의 value에서 이러한 메소드를 호출 할 때 PSwift는 기본 콘크리트 유형을 발굴하고이를 사용하여 Self일반 자리 표시자를 만족시킵니다 . 우리가 호출 할 수있는 이유입니다 takesConcreteP(_:)함께 self우리가 만족하고 - TSelf.

이것은 우리가 지금 말할 수 있음을 의미합니다 :

p.callTakesConcreteP()

그리고 기본 콘크리트 유형 (이 경우 ) 에 의해 takesConcreteP(_:)일반 자리 표시자가 T충족되어 호출됩니다 S. 구체적인 유형을 대체하는 것이 아니라 "자체에 맞는 프로토콜"이 아닙니다. 프로토콜 P에 정적 요구 사항을 추가하고 내부에서 호출 할 때 발생하는 상황을 확인하십시오 takesConcreteP(_:).

Swift가 프로토콜이 자체 규정을 준수하지 못하게하는 경우, 다음 최선의 대안은 프로토콜을 일반 유형의 매개 변수에 대한 인수로 전달하려고 할 때 암시 적으로 존재 성을 여는 것입니다.

그러나 존재를 여는 것이 프로토콜 자체에 맞지 않는 문제에 대한 일반적인 해결책은 아닙니다. 프로토콜 유형 값의 이기종 콜렉션을 다루지 않으며, 각각 다른 기본 콘크리트 유형을 가질 수 있습니다. 예를 들어, 다음을 고려하십시오.

struct Q : P {
  var bar: Int
  func foo(str: String) {}
}

// The placeholder `T` must be satisfied by a single type
func takesConcreteArrayOfP<T : P>(_ t: [T]) {}

// ...but an array of `P` could have elements of different underlying concrete types.
let array: [P] = [S(bar: 1), Q(bar: 2)]

// So there's no sensible concrete type we can substitute for `T`.
takesConcreteArrayOfP(array) 

같은 이유로 여러 T매개 변수를 가진 함수 도 문제가 될 수 있습니다. 매개 변수는 동일한 유형의 인수를 가져와야합니다. 그러나 P값 이 두 개인 경우 컴파일 타임에 둘 다 동일한 기본 콘크리트를 가지고 있다고 보장 할 수있는 방법이 없습니다 유형.

이 문제를 해결하기 위해 타입 지우개를 사용할 수 있습니다.

2. 지우개 작성

마찬가지로 롭 말한다 하는 타입 지우개 , 프로토콜 자체에 부합하지 않는 문제에 대한 가장 일반적인 솔루션입니다. 이를 통해 인스턴스 요구 사항을 기본 인스턴스로 전달하여 프로토콜 유형의 인스턴스를 해당 프로토콜에 맞는 구체적인 유형으로 래핑 할 수 있습니다.

따라서 P인스턴스 요구 사항을 다음과 같은 기본 임의 인스턴스에 전달하는 유형 지우기 상자를 작성해 보겠습니다 P.

struct AnyP : P {

  private var base: P

  init(_ base: P) {
    self.base = base
  }

  var bar: Int {
    get { return base.bar }
    set { base.bar = newValue }
  }

  func foo(str: String) { base.foo(str: str) }
}

이제 우리는 AnyP대신에 P다음 과 같이 말할 수 있습니다 .

let p = AnyP(S(bar: 5))
takesConcreteP(p)

// example from #1...
let array = [AnyP(S(bar: 1)), AnyP(Q(bar: 2))]
takesConcreteArrayOfP(array)

이제 왜 우리가 그 상자를 만들어야하는지 잠시 생각해보십시오. 앞서 논의했듯이 Swift에는 프로토콜에 정적 요구 사항이있는 경우 구체적인 유형이 필요합니다. P정적 요구 사항이 있는지 고려하십시오 –에 구현해야했습니다 AnyP. 그러나 무엇으로 구현 되었습니까? P여기서 준수하는 임의의 인스턴스를 처리 하고 있습니다. 기본 콘크리트 유형이 정적 요구 사항을 구현하는 방법을 알지 못하므로이를로 의미있게 표현할 수 없습니다 AnyP.

따라서이 경우 솔루션은 인스턴스 프로토콜 요구 사항 의 경우에만 실제로 유용 합니다. 일반적인 경우, 우리는 여전히을 P따르는 구체적인 유형으로 취급 할 수 없습니다 P.


2
어쩌면 나는 밀도가 높지만 정적 사례가 왜 특별한 지 이해할 수 없습니다. 우리 (컴파일러)는 프로토콜의 인스턴스 속성, 즉 채택자가 구현할 것이라는 것을 알 때 컴파일 타임에 프로토의 정적 속성에 대해 거의 또는 거의 알지 못합니다. 차이점은 무엇입니까?
matt

1
@matt 프로토콜 유형의 인스턴스 (즉, 실재로 래핑 된 콘크리트 유형의 인스턴스 P)는 인스턴스 요구 사항에 대한 호출을 기본 인스턴스로 전달할 수 있기 때문에 좋습니다. 그러나, 프로토콜 유형에 대한 자체 (즉 P.Protocol프로토콜을 설명 그대로 단지 형) - 더 채택이 없다, 그러므로 우리가 할 수없는 이유 위의 예에에 정적 요구 사항을 호출하는 것도, 없다 SomeGeneric<P>(그것은이다 P.Type(존재하는 메타 타입)과 는 다른, (존재하는 메타 타입), P다른 것을 따르는 구체적인 메타 타입을 설명 하지만 – 또 다른 이야기입니다)
Hamish

이 페이지 상단에서 묻는 질문은 프로토콜 유형 채택자가 좋은 이유와 프로토콜 유형 자체가 아닌 이유입니다. 프로토콜 유형 자체에는 채택자가 없다는 것을 알고 있습니다. — 이해하지 못하는 것은 인스턴스 호출을 채택 유형으로 전달하는 것보다 정적 호출을 채택 유형으로 전달하는 것이 더 어려운 이유입니다. 여기에 어려움이있는 이유는 특히 정적 요구 사항의 특성 때문이라고 주장하지만 정적 요구 사항이 인스턴스 요구 사항보다 얼마나 어려운지는 알 수 없습니다.
matt

@matt 정적 요구 사항이 인스턴스 요구 사항보다 "단단한"것은 아닙니다. 컴파일러는 인스턴스의 존재 (예 :로 유형이 지정된 인스턴스 P)와 실존 메타 유형 (예 : P.Type메타 유형 )을 통해 정교하게 처리 할 수 ​​있습니다 . 문제는 제네릭의 경우 – 우리는 실제로 같은 것을 비교하지 않는다는 것입니다. 때 T이다 P, 포워드 정적 요구 사항에 더 underyling 콘크리트 (메타) 유형 (없다 TP.Protocol이 아닌 P.Type) ....
해미

1
나는 실제로 건전성 등을 신경 쓰지 않고 단지 앱을 작성하고 싶습니다. 작동해야한다고 생각되면 제대로 작동해야합니다. 언어는 제품 자체가 아니라 도구 일뿐입니다. 실제로 작동하지 않는 경우가 있다면 그 경우에는 허용하지 않지만 다른 사람은 해당 사례를 사용하고 앱 작성을 계속하도록하십시오.
조나단.

17

구체적인 유형으로 CollectionType프로토콜 대신 프로토콜 을 확장하고 프로토콜 Array별로 제한하는 경우 다음과 같이 이전 코드를 다시 작성할 수 있습니다.

protocol P { }
struct S: P { }

let arr:[P] = [ S() ]

extension CollectionType where Generator.Element == P {
    func test<T>() -> [T] {
        return []
    }
}

let result : [S] = arr.test()

Collection vs Array는 여기서 관련이 없다고 생각합니다. 중요한 변경은 == Pvs를 사용하는 것 : P입니다. ==를 사용하면 원래 예제도 작동합니다. 나는를 만들 경우 : 그리고 ==와 잠재적 인 문제 (상황에 따라)는 그것을 제외 하위 프로토콜이다 protocol SubP: P,하고 정의 arr[SubP]다음 arr.test()(: SUBP와 P가 동등해야 오류가) 더 이상 작동하지 않습니다.
임레
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.