some View
olan opak bir sonuç türü tarafından tanıtılan olarak SE-0244 ve bir "geri" genel yer tutucu olarak bu düşünebiliriz Xcode 11. Sizinle Swift 5.1 mevcuttur.
Arayan tarafından tatmin edilen normal bir genel yer tutucunun aksine:
protocol P {}
struct S1 : P {}
struct S2 : P {}
func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.
Opak bir sonuç türü, uygulamadan memnun olan örtük bir genel yer tutucudur , bu yüzden bunu düşünebilirsiniz:
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
şöyle görünüyor:
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
Aslında, bu özelliğin nihai amacı, bu daha açık biçimde ters jeneriklere izin vermektir, bu da sınırlamalar eklemenize izin verir, örn -> <T : Collection> T where T.Element == Int
. Daha fazla bilgi için bu gönderiye bakın .
Bundan uzaklaşacak en önemli şey, geri dönen bir işlevin , uygun olan tek bir somut tipin some P
değerini döndüren bir işlev olmasıdır . İşlev içinde farklı uygun türleri döndürmeye çalışmak derleyici hatası verir:P
// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Örtük genel yer tutucu birden çok tip tarafından karşılanamadığından.
Bu, her ikisiniP
temsil etmek için kullanılabilen ve keyfi bir uyumluluk değerini temsil ettiği için dönen bir işlevin tersidir : S1
S2
P
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Peki, opak sonuç türlerinin -> some P
protokol dönüş türlerine göre ne gibi faydaları vardır -> P
?
1. Opak sonuç tipleri PAT'lerle kullanılabilir
Protokollerin önemli bir akım sınırlaması, PAT'lerin (ilişkili türlerle protokoller) gerçek türler olarak kullanılamamasıdır. Bu, dilin gelecekteki bir versiyonunda kaldırılacak bir kısıtlama olsa da, opak sonuç türleri etkili bir şekilde sadece genel yer tutucular olduğundan, günümüzde PAT'lerle kullanılabilirler.
Bu, aşağıdakileri yapabileceğiniz anlamına gelir:
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2. Opak sonuç türlerinin kimliği vardır
Opak sonuç türleri tek bir beton türü döndürdüğünden, derleyici aynı işleve iki çağrının aynı türden iki değer döndürmesi gerektiğini bilir.
Bu, aşağıdakileri yapabileceğiniz anlamına gelir:
// foo() -> <Output : Equatable> Output {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.
Bu yasaldır çünkü derleyici her ikisini de bilir x
ve y
aynı beton tipine sahiptir. Bu, ==
her iki tip parametrenin de bulunduğu önemli bir gereksinimdir Self
.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
Bu, beton uyumlu tiple aynı tipte iki değer beklediği anlamına gelir. Equatable
Bir tür olarak kullanılabilse bile , iki rastgele Equatable
uyumlu değeri birbiriyle karşılaştıramazsınız , örneğin:
func foo(_ x: Int) -> Equatable { // Assume this is legal.
if x > 10 {
return 0
} else {
return "hello world"
}
}
let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.
Derleyici, iki keyfi Equatable
değerin aynı temel beton tipine sahip olduğunu kanıtlayamaz .
Benzer bir şekilde, başka bir opak tip geri döndürme fonksiyonu getirdiysek:
// foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
// bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable {
return "" // The opaque result type is inferred to be String.
}
let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.
Her iki halde çünkü örnek yasadışı hale gelir foo
ve bar
getiri some Equatable
, onların jenerik tutucuları "ters" Output1
ve Output2
farklı türde suretiyle yerine getirilebileceğini.
3. Opak sonuç türleri jenerik yer tutucularla oluşturulur
Protokol tipindeki normal değerlerin aksine, opak sonuç türleri düzenli genel yer tutucularla iyi uyum sağlar, örneğin:
protocol P {
var i: Int { get }
}
struct S : P {
var i: Int
}
func makeP() -> some P { // Opaque result type inferred to be S.
return S(i: .random(in: 0 ..< 10))
}
func bar<T : P>(_ x: T, _ y: T) -> T {
return x.i < y.i ? x : y
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.
İki değer farklı temel beton türlerine sahip olabileceğinden , makeP
yeni dönmüş olsaydı bu işe yaramazdı P
, P
örneğin:
struct T : P {
var i: Int
}
func makeP() -> P {
if .random() { // 50:50 chance of picking each branch.
return S(i: 0)
} else {
return T(i: 1)
}
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.
Beton türü üzerinde neden opak bir sonuç türü kullanılmalı?
Bu noktada, kendiniz düşünüyor olabilirsiniz, neden kodu sadece şu şekilde yazmıyorsunuz:
func makeP() -> S {
return S(i: 0)
}
Opak bir sonuç türünün kullanılması, S
yalnızca sağlanan arabirimi açığa çıkararak P
, işleve bağlı herhangi bir kodu kırmadan beton türünü daha sonra çizginin aşağısına değiştirme esnekliği sağlayarak , türü bir uygulama ayrıntısı yapmanıza olanak tanır .
Örneğin, aşağıdakileri değiştirebilirsiniz:
func makeP() -> some P {
return S(i: 0)
}
ile:
func makeP() -> some P {
return T(i: 1)
}
çağıran herhangi bir kodu kırmadan makeP()
.
Bkz Opak Çeşitleri bölümü dil kılavuzun ve Swift evrimi önerisini Bu özellik hakkında daha fazla bilgi için.