Protokol kendine uymuyor mu?


126

Bu Swift kodu neden derlenmiyor?

protocol P { }
struct S: P { }

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

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

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

Derleyici şunu söyler: "Tür Pprotokole uymuyor P" (veya Swift'in sonraki sürümlerinde "P" protokolünü 'P' protokolüne uygun somut bir tür olarak kullanmak desteklenmiyor. ").

Neden olmasın? Bu bir şekilde dilde bir delik gibi geliyor. Sorunun arrdiziyi bir protokol türü dizisi olarak bildirmekten kaynaklandığını anlıyorum , ancak bu mantıksız bir şey mi? Protokollerin, yapılara tam olarak bir tür hiyerarşisi gibi bir şey sağlamaya yardımcı olmak için orada olduğunu düşündüm.


1
let arrSatırdaki tür ek açıklamasını kaldırdığınızda, derleyici türü belirler [S]ve kod derlenir. Görünüşe göre bir protokol türü, bir sınıf - süper sınıf ilişkisi ile aynı şekilde kullanılamaz.
Vadian

1
@vadian Correct, "Sorunun arr dizisini bir protokol türü dizisi olarak bildirmekten kaynaklandığını anladım" dediğimde sorumda bahsettiğim şey buydu. Sorumun söylemek devam Fakat, protokollerin bütün mesele genellikle olmasıdır edebilirsiniz sınıf olarak aynı şekilde kullanılabilir - üst sınıf ilişki! Onlar edilir amaçlanan yapılar dünyasına hiyerarşik yapı bir tür sağlamaktır. Ve genellikle yaparlar. Soru şu ki, bu neden burada çalışmasın ?
Matt

1
Xcode 7.1'de hala çalışmıyor, ancak hata mesajı artık "'P' protokolüne uygun somut bir tür olarak 'P' kullanılması desteklenmiyor" .
Martin R

1
@MartinR Daha iyi bir hata mesajı. Ama yine de bana dilde bir delik gibi geliyor.
mat

Elbette! Hatta ile protocol P : Q { }P Q uymayan
Martin R

Yanıtlar:


66

DÜZENLEME: On sekiz ay daha Swift ile çalışma, başka bir büyük sürüm (yeni bir teşhis sağlayan) ve @ AyBayBay'den bir yorum bu cevabı yeniden yazmak istememe neden oluyor. Yeni teşhis:

"'P'nin' P 'protokolüne uyan somut bir tür olarak kullanılması desteklenmez."

Bu aslında her şeyi çok daha net hale getiriyor. Bu uzantı:

extension Array where Element : P {

ne zaman geçerli değildir Element == Pçünkü Psomut bir uygunluk kabul edilmez P. (Aşağıdaki "bir kutuya koyun" çözümü hala en genel çözümdür.)


Eski Cevap:

Yine başka bir metatip durumu. Swift, önemsiz olmayan şeylerin çoğu için somut bir türe sahip olmanı gerçekten istiyor. [P]somut bir tür değildir (bilinen boyutta bir bellek bloğu ayıramazsınız P). (Bunun gerçekten doğru olduğunu sanmıyorum; kesinlikle büyüklükte bir şey yaratabilirsiniz Pçünkü dolaylı yoldan yapılır .) Bunun işe yaramaması gerektiğine dair herhangi bir kanıt olduğunu sanmıyorum. Bu, "henüz işe yaramıyor" vakalarından birine çok benziyor. (Maalesef, Apple'ın bu durumlar arasındaki farkı doğrulamasını sağlamak neredeyse imkansızdır.) Array<P>Değişken bir tür olabileceği gerçeği (buradaArraycan't), bu yönde zaten bazı çalışmalar yaptıklarını, ancak Swift metatiplerinin çok sayıda keskin kenarı ve uygulanmamış durumları olduğunu gösterir. Bundan daha iyi bir "neden" cevabı alacağınızı sanmıyorum. "Çünkü derleyici buna izin vermiyor." (Tatmin edici değil, biliyorum. Tüm Swift hayatım…)

Çözüm, neredeyse her zaman bir şeyi bir kutuya koymaktır. Bir tür silgi oluşturuyoruz.

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 bunu doğrudan yapmanıza izin verdiğinde (eninde sonunda bunu bekliyorum), muhtemelen bu kutuyu sizin için otomatik olarak oluşturarak olacaktır. Özyinelemeli numaralandırmalar tam olarak bu geçmişe sahipti. Onları kutuya koymanız gerekiyordu ve bu inanılmaz derecede sinir bozucu ve kısıtlayıcıydı ve sonunda derleyici indirectaynı şeyi daha otomatik yapmak için eklendi .


Bu cevapta pek çok yararlı bilgi var, ancak Tomohiro'nun cevabındaki gerçek çözüm, burada sunulan boks çözümünden daha iyi.
jsadler

@jsadler Soru, sınırlamanın nasıl aşılacağı değil, sınırlamanın neden var olduğu. Gerçekten de, açıklamaya göre, Tomohiro'nun çözümü yanıtladığından daha fazla soru ortaya çıkarıyor. ==Benim Array örneğimde kullanırsak , bir hata alırız, Aynı tür gereksinimi 'Element' genel parametresini genel olmayan yapar. "Tomohiro'nun kullanımı neden ==aynı hatayı oluşturmaz?
matt

@Rob Napier Cevabınız hala kafamı karıştırıyor. Swift, çözümünüzde orijinaline kıyasla daha fazla somutluk görüyor mu? Her şeyi bir yapıya sarmış gibiydin ... Idk belki hızlı tip sistemi anlamakta zorlanıyorum ama bunların hepsi sihirli vudu gibi görünüyor
AyBayBay

@AyBayBay Yanıt güncellendi.
Rob Napier

Çok teşekkür ederim @RobNapier Cevaplarınızın hızına ve açıkçası insanlara yardım etmek için en az sizin kadar zaman bulduğunuza her zaman hayret ediyorum. Yine de yeni düzenlemeleriniz kesinlikle onu bir perspektife oturtuyor. Belirtmek istediğim bir şey daha, yazı silmeyi anlamak da bana yardımcı oldu. Bu makale özellikle harika bir iş çıkardı: krakendev.io/blog/generic-protocols-and-their-shortcomings TBH Idk, bunlardan bazıları hakkında ne hissettiğimi. Görünüşe göre dildeki boşlukları açıklıyoruz ama İdk elmanın bunun bir kısmını nasıl inşa edeceğini.
AyBayBay

110

Protokoller neden kendilerine uymuyor?

Genel durumda protokollerin kendilerine uymasına izin vermek sağlam değildir. Sorun, statik protokol gereksinimlerinden kaynaklanmaktadır.

Bunlar şunları içerir:

  • static yöntemler ve özellikler
  • olan başlatıcı
  • İlişkili türler (bunlar şu anda bir protokolün gerçek bir tür olarak kullanılmasını engellemesine rağmen)

Biz genel bir yer tutucu üzerinde bu gereksinimleri erişebilir ancak biz - edemez üzerine iletmek için hiçbir somut uygun tip var gibi protokol türü kendisinde erişin. Bu nedenle izin veremez olmak .TT : PTP

ArrayUzantının uygulanabilir olmasına izin verirsek aşağıdaki örnekte ne olacağını düşünün [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()

Biz muhtemelen arayamam appendNew()bir üstünde [P], çünkü P( Element) somut bir türü değil ve bu nedenle başlatılamaz. Bu gereken beton yazılan elemanları, bu tür uygundur için bir dizi çağrılabilir P.

Statik yöntem ve özellik gereksinimleri olan benzer bir hikaye:

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()

Açısından konuşamayız SomeGeneric<P>. Statik protokol gereksinimlerinin somut uygulamalarına ihtiyacımız var ( yukarıdaki örnekte hiçbir uygulamanın foo()veya bartanımlamanın olmadığına dikkat edin ). Bu gereksinimlerin uygulamalarını bir Puzantıda tanımlayabilsek de , bunlar yalnızca uygun olan somut türler için tanımlanmıştır P- yine de bunları Pkendi başına arayamazsınız.

Bu nedenle Swift, bir protokolü kendisine uyan bir tür olarak kullanmamıza tamamen izin vermiyor - çünkü bu protokolün statik gereksinimleri olduğunda, öyle değil.

Örnek protokol gereksinimleri sorunlu değildir, çünkü bunları protokole uyan gerçek bir örnekte çağırmanız gerekir (ve bu nedenle gereksinimleri uygulamış olmanız gerekir). Dolayısıyla, olarak yazılan bir örnekte bir gereksinimi çağırırken P, bu çağrıyı temeldeki somut türün bu gereksinimin uygulanmasına iletebiliriz.

Bununla birlikte, bu durumda kural için özel istisnalar yapmak, protokollerin genel kod tarafından nasıl ele alındığı konusunda şaşırtıcı tutarsızlıklara yol açabilir. Her ne kadar söyleniyor olsa da, durum associatedtypegereksinimlerden çok farklı değildir - bu (şu anda) bir tür olarak bir protokolü kullanmanızı engellemektedir. Statik gereksinimleri olduğunda kendine uyan bir tür olarak bir protokolü kullanmanızı engelleyen bir kısıtlamaya sahip olmak, dilin gelecekteki bir sürümü için bir seçenek olabilir.

Düzenleme: Aşağıda incelendiği gibi, bu Swift ekibinin hedeflediği şeye benziyor.


@objc protokolleri

Ve aslında, dil protokollere tam olarak böyle davranır @objc. Statik gereksinimleri olmadığında kendilerine uyarlar.

Aşağıdaki derlemeler gayet iyi:

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)

bazTuygun olmasını gerektirir P; ama biz yerini alabilir Piçin Tçünkü Pstatik gereksinimleri yoktur. Öğesine statik bir gereksinim eklersek P, örnek artık derlemez:

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)'

Yani bu soruna bir çözüm protokolünüzü yapmaktır @objc. Kabul edilirse, bu çoğu durumda ideal bir çözüm değildir, çünkü uyumlu türlerinizi sınıflar olmaya zorlar ve ayrıca Obj-C çalışma zamanını gerektirir, bu nedenle Linux gibi Apple dışı platformlarda uygulanabilir hale getirmez.

Ancak, bu sınırlamanın, dilin zaten protokoller için 'statik gereksinimleri olmayan protokolün kendisine uymasının ana nedenlerinden biri olduğundan şüpheleniyorum @objc. Çevresine yazılan genel kod, derleyici tarafından önemli ölçüde basitleştirilebilir.

Neden? Çünkü @objcprotokol tipi değerler, gereksinimleri kullanılarak gönderilen yalnızca sınıf referanslarıdır objc_msgSend. Diğer taraftan, @objchem (potansiyel olarak dolaylı olarak depolanmış) sarılmış değerlerinin belleğini yönetmek hem de farklı için hangi uygulamaların çağrılacağını belirlemek için hem değer hem de tanık tabloları taşıdıkları için protokol tipi olmayan değerler daha karmaşıktır. sırasıyla gereksinimleri.

Çünkü bu basitleştirilmiş temsil @objcprotokoller, böyle bir protokol tip bir değer Ptüründe bir 'jenerik değeri' bazı genel yer tutucu ile aynı bellek gösterimi paylaşabilir T : P, muhtemelen kendine uygunluğunu sağlamak için Swift takımın kolay hale getirmektedir. Aynı durum @objcprotokol olmayanlar için geçerli değildir, ancak bu tür genel değerler şu anda değer veya protokol tanık tabloları taşımamaktadır.

Ancak bu özellik olduğu kasıtlı ve sivil kullanıma sunulacak için umutla olan @objcSwift ekip üyesi Slava Pestov doğruladığı gibi, protokoller SR-55 yorumlarında o (tarafından istendiğinde ilgili sorguya yanıt olarak bu soruya ):

Matt Neuburg bir yorum ekledi - 7 Eyl 2017 13:33

Bu derleme yapar:

@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) }

Eklemek @objconu derlemesini sağlar; kaldırılması, yeniden derlenmemesini sağlar. Stack Overflow'daki bazılarımız bunu şaşırtıcı buluyor ve bunun kasıtlı mı yoksa hatalı bir durum mu olduğunu bilmek istiyoruz.

Slava Pestov yorum ekledi - 7 Eyl 2017 13:53

Kasıtlı - bu kısıtlamanın kaldırılması, bu hatanın konusu. Dediğim gibi aldatıcı ve henüz somut bir planımız yok.

Bu yüzden umarım bu dilin bir gün @objcprotokol olmayanlar için de destekleyeceği bir şeydir .

Ama @objcprotokol olmayanlar için hangi güncel çözümler var ?


Protokol kısıtlamalarıyla uzantıların uygulanması

Swift 3.1'de, belirli bir genel yer tutucunun veya ilişkili türün belirli bir protokol türü olması gerektiğine (yalnızca bu protokole uyan somut bir tür değil) yönelik bir kısıtlamaya sahip bir uzantı istiyorsanız - bunu basitçe bir ==kısıtlama ile tanımlayabilirsiniz .

Örneğin, dizi uzantınızı şu şekilde yazabiliriz:

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

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

Elbette, bu artık onu uyumlu somut tip elemanlara sahip bir dizi üzerinde çağırmamızı engelliyor P. Biz sadece için ek bir uzantı tanımlayarak bu çözebilir Element : Püzerine sadece ileriye ve == Puzantısı:

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

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

Bununla birlikte [P], her bir elemanın varoluşsal bir kapta kutuya alınması gerekeceğinden, bunun dizinin bir O (n) dönüşümünü gerçekleştireceğini belirtmek gerekir. Performans bir sorunsa, bunu uzantı yöntemini yeniden uygulayarak kolayca çözebilirsiniz. Bu, tamamen tatmin edici bir çözüm değildir - umarız dilin gelecekteki bir sürümü, bir 'protokol türünü veya protokol türüne uygun' kısıtlamasını ifade etmenin bir yolunu içerecektir .

Swift 3.1'den önce, bunu başarmanın en genel yolu, Rob'un cevabında gösterdiği gibi , basitçe a için bir sarmalayıcı türü oluşturmaktır [P], bu daha sonra uzatma yöntemlerinizi tanımlayabilirsiniz.


Protokol türü bir örneği kısıtlanmış genel bir yer tutucuya iletme

Aşağıdaki (yapmacık ama nadir olmayan) durumu düşünün:

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)

Biz geçemez piçin takesConcreteP(_:)şu anda yerini alamaz gibi Pgenel bir yer tutucu için T : P. Bu sorunu çözebileceğimiz birkaç yola bir göz atalım.

1. Varoluşları açma

Aksine yerine çalışmak yerine Piçin T : P, biz altta yatan beton türü içine ne kazmak ki, eğer PYazılan değer yerine o sarma ve yedek oldu? Maalesef bu , şu anda doğrudan kullanıcılar tarafından kullanılamayan varoluş bilgilerini açma adı verilen bir dil özelliği gerektirir .

Ancak, Swift , üyelere erişirken varoluş bilgilerini (protokol-tipli değerler) örtük olarak açar (yani, çalışma zamanı tipini çıkarır ve onu genel bir yer tutucu şeklinde erişilebilir kılar). Bu gerçeği aşağıdaki protokol uzantısında kullanabiliriz P:

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

SelfÖrtük selfparametreyi yazmak için kullanılan uzantı yönteminin aldığı örtük genel yer tutucuyu not edin - bu, tüm protokol uzantısı üyeleriyle perde arkasında gerçekleşir. Protokol tipli bir değerde böyle bir yöntemi çağırırken P, Swift temeldeki somut türü ortaya çıkarır ve bunu Selfgenel yer tutucuyu tatmin etmek için kullanır . Dediğimiz edebiliyoruz nedeni budur takesConcreteP(_:)ile selfbiz tatmin ediyoruz - Tile Self.

Bu, artık şunu söyleyebileceğimiz anlamına gelir:

p.callTakesConcreteP()

Ve takesConcreteP(_:)genel yer tutucusunun Ttemeldeki somut türden tatmin olmasıyla çağrılır (bu durumda S). Bunun "kendilerine uyan protokoller" olmadığına dikkat edin, çünkü biz Pprotokole statik bir gereksinim eklemeyi deneyin ve onu içeriden çağırdığınızda ne olduğunu görmeyi deneyin takesConcreteP(_:).

Swift, protokollerin kendilerine uymasını engellemeye devam ederse, bir sonraki en iyi alternatif, onları genel tipteki parametrelere argüman olarak aktarmaya çalışırken örtük olarak varoluşları açmaktır - etkili bir şekilde protokol uzantımız trambolininin yaptığını, sadece şablon olmadan yapmak.

Bununla birlikte, varoluş bilgilerini açmanın, kendilerine uymayan protokoller sorununa genel bir çözüm olmadığını unutmayın. Hepsi farklı temel somut türlere sahip olabilen, protokol tipli değerlerin heterojen koleksiyonlarıyla ilgilenmez. Örneğin, şunları göz önünde bulundurun:

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) 

Aynı nedenlerden ötürü, birden çok Tparametresi olan bir işlev de sorunlu olabilir, çünkü parametreler aynı türde argümanlar almalıdır - ancak iki Pdeğerimiz varsa, derleme sırasında ikisinin de aynı temel somuta sahip olduğunu garanti etmemiz mümkün değildir. yazın.

Bu sorunu çözmek için bir tip silgi kullanabiliriz.

2. Bir tür silgi oluşturun

As Rob diyor , bir tür silgi , protokoller kendilerine uygun değil sorununa en genel çözümdür. Örnek gereksinimlerini temeldeki örneğe ileterek, protokol tipi bir örneği bu protokole uyan somut bir türle sarmamıza olanak tanırlar.

Öyleyse, Pörnek gereksinimlerini aşağıdakilere uyan alttaki rastgele bir örneğe ileten bir tür silme kutusu oluşturalım 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) }
}

Şimdi AnyPbunun yerine şu terimlerle konuşabiliriz P:

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

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

Şimdi, bir an için o kutuyu neden inşa etmemiz gerektiğini düşünün. Daha önce tartıştığımız gibi, protokolün statik gereksinimlere sahip olduğu durumlar için Swift'in somut bir türe ihtiyacı vardır. PStatik bir gereksiniminiz olup olmadığını düşünün - bunu uygulamamız gerekirdi AnyP. Ama ne olarak uygulanmalıydı? Buraya uyan keyfi örneklerle uğraşıyoruz P- bunların temelindeki somut türlerin statik gereksinimleri nasıl uyguladığını bilmiyoruz, bu nedenle bunu anlamlı bir şekilde ifade edemiyoruz AnyP.

Bu nedenle, bu durumda çözüm, yalnızca örnek protokol gereksinimleri durumunda gerçekten yararlıdır . Genel durumda, yine de Puygun olan somut bir tür olarak ele alamayız P.


2
Belki sadece yoğun oluyorum ama statik durumun neden özel olduğunu anlamıyorum. Biz (derleyici), bir protokolün örnek özelliği hakkında bildiğimiz kadar derleme zamanında bir prototolün statik özelliği hakkında, yani benimseyen kişinin onu uygulayacağı kadar çok veya az şey biliyoruz. Öyleyse fark nedir?
mat

1
@matt Protokol tipi bir örnek (yani varoluşsal olarak sarmalanmış somut tipte örnek P) iyidir, çünkü çağrı gereksinimlerini temel alınan örneğe yönlendirebiliriz. Bununla birlikte, bir protokol türünün kendisi için (yani P.Protocol, kelimenin tam anlamıyla bir protokolü tanımlayan tür) - benimseyen yoktur, bu nedenle statik gereksinimleri çağıracak hiçbir şey yoktur, bu nedenle yukarıdaki örnekte sahip olamayız SomeGeneric<P>(Bu, P.Typeuygun olan bir şeyin somut bir metatipini tanımlayan bir (varoluşsal metatip) için farklı P- ama bu başka bir hikaye)
Hamish

Bu sayfanın en üstünde sorduğum soru, neden bir protokol türü benimseyen iyidir ve bir protokol türünün kendisi değildir. Bir protokol türünün kendisi için benimseyen olmadığını anlıyorum. - Anlayamadığım şey, neden statik çağrıları benimseyen türe yönlendirmenin, örnek çağrılarını benimseyen türe yönlendirmekten daha zor olduğu. Burada bir zorluğun sebebinin özellikle statik gereksinimlerin doğasından kaynaklandığını iddia ediyorsunuz, ancak statik gereksinimlerin örnek gereksinimlerinden daha zor olduğunu görmüyorum.
mat

@matt Statik gereksinimler, örnek gereksinimlerinden "daha zor" değildir - derleyici, hem örnekler Piçin varoluşsal konularla (örneğin, örnek olarak yazılır ) hem de varoluşsal meta türlerle (ör. P.Typemetatipler) iyi işleyebilir. Sorun şu ki, jenerikler için - benzerleri gibi karşılaştırmıyoruz. Olduğunda T, Pstatik gereksinimleri ( Ta P.Protocoldeğil , a değil P.Type) iletmek için erimeyen beton (meta) türü yoktur ....
Hamish

1
Sağlamlık vb. Umurumda değil, sadece uygulamalar yazmak istiyorum ve eğer çalışacakmış gibi geliyorsa, sadece yapmalı. Dil, bir ürünün kendisi değil, sadece bir araç olmalıdır. Gerçekten işe yaramayacağı bazı durumlar varsa, bu durumlarda buna izin vermeyin, ancak herkesin çalıştığı vakaları kullanmasına izin verin ve uygulama yazmaya devam etmelerine izin verin.
Jonathan.

17

Somut bir tür olarak protokole göre protokol ve kısıtlama CollectionTypeyerine protokolü genişletirseniz Array, önceki kodu aşağıdaki gibi yeniden yazabilirsiniz.

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'in burada alakalı olduğunu düşünmüyorum, önemli değişiklik == Pvs kullanmaktır : P. == ile orijinal örnek de işe yarar. Ben oluşturursanız: Ve == ile potansiyel bir sorun (bağlama göre) ittiğini alt protokolleri olduğunu protocol SubP: Pve sonra tanımlamak arrolarak [SubP]daha sonra arr.test()(: SubP ve P eşdeğeri olmalıdır hatası) artık çalışmaz.
imre
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.