some View
adalah tipe hasil buram seperti yang diperkenalkan oleh SE-0244 dan tersedia di Swift 5.1 dengan Xcode 11. Anda dapat menganggap ini sebagai pengganti generik "terbalik".
Tidak seperti placeholder generik reguler yang dipenuhi oleh penelepon:
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.
Jenis hasil buram adalah tempat penampung generik implisit puas dengan implementasi , sehingga Anda bisa memikirkan ini:
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
terlihat seperti ini:
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
Bahkan, tujuan akhirnya dengan fitur ini adalah untuk memungkinkan pembalikan generik dalam bentuk yang lebih eksplisit ini, yang juga memungkinkan Anda menambahkan batasan, misalnya -> <T : Collection> T where T.Element == Int
. Lihat posting ini untuk info lebih lanjut .
Hal utama yang harus diambil dari hal ini adalah bahwa fungsi yang dikembalikan some P
adalah yang mengembalikan nilai jenis beton tunggal tertentu yang sesuai P
. Mencoba untuk mengembalikan berbagai jenis yang sesuai dalam fungsi menghasilkan kesalahan kompilator:
// 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()
}
}
Karena placeholder generik implisit tidak dapat dipenuhi oleh beberapa tipe.
Ini berbeda dengan fungsi yang dikembalikan P
, yang dapat digunakan untuk merepresentasikan keduanya S1
dan S2
karena itu merepresentasikan P
nilai kesesuaian yang berubah-ubah :
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Oke, jadi manfaat apa yang -> some P
dimiliki tipe hasil buram daripada tipe protokol kembali -> P
?
1. Jenis hasil buram dapat digunakan dengan PAT
Keterbatasan protokol saat ini adalah bahwa PATs (protokol dengan tipe terkait) tidak dapat digunakan sebagai tipe aktual. Meskipun ini adalah batasan yang kemungkinan akan diangkat dalam versi bahasa yang akan datang, karena jenis hasil buram secara efektif hanya penampung generik, mereka dapat digunakan dengan PATs hari ini.
Ini berarti Anda dapat melakukan hal-hal seperti:
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2. Jenis hasil buram memiliki identitas
Karena tipe hasil buram menegakkan tipe beton tunggal dikembalikan, kompiler tahu bahwa dua panggilan ke fungsi yang sama harus mengembalikan dua nilai dari jenis yang sama.
Ini berarti Anda dapat melakukan hal-hal seperti:
// 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.
Ini legal karena kompiler mengetahui keduanya x
dan y
memiliki tipe beton yang sama. Ini merupakan persyaratan penting untuk ==
, di mana kedua parameter jenis Self
.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
Ini berarti bahwa ia mengharapkan dua nilai yang sama-sama bertipe sama dengan tipe konformasi beton. Bahkan jika Equatable
dapat digunakan sebagai tipe, Anda tidak akan dapat membandingkan dua Equatable
nilai kesesuaian yang berubah-ubah satu sama lain, misalnya:
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.
Sebagai kompiler tidak dapat membuktikan bahwa dua sewenang-wenang Equatable
nilai memiliki tipe beton mendasar yang sama.
Dengan cara yang sama, jika kami memperkenalkan fungsi pengembalian tipe buram lainnya:
// 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.
Contoh menjadi ilegal karena meskipun keduanya foo
dan bar
kembali some Equatable
, mereka "membalikkan" penampung generik Output1
dan Output2
dapat dipenuhi oleh berbagai jenis.
3. Jenis hasil buram menulis dengan penampung generik
Tidak seperti nilai yang diketikkan dengan protokol biasa, tipe hasil buram cocok dengan placeholder generik biasa, misalnya:
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.
Ini tidak akan berhasil jika makeP
baru saja kembali P
, karena dua P
nilai mungkin memiliki tipe beton dasar yang berbeda, misalnya:
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.
Mengapa menggunakan jenis hasil buram di atas jenis beton?
Pada titik ini Anda mungkin berpikir untuk diri sendiri, mengapa tidak menulis kode saja sebagai:
func makeP() -> S {
return S(i: 0)
}
Nah, penggunaan jenis hasil buram memungkinkan Anda untuk membuat jenis S
detail implementasi dengan hanya mengekspos antarmuka yang disediakan olehP
, memberi Anda fleksibilitas untuk mengubah jenis beton di kemudian hari tanpa melanggar kode apa pun yang tergantung pada fungsi.
Misalnya, Anda dapat mengganti:
func makeP() -> some P {
return S(i: 0)
}
dengan:
func makeP() -> some P {
return T(i: 1)
}
tanpa melanggar kode apa pun yang memanggil makeP()
.
Lihat bagian Jenis Buram dari panduan bahasa dan proposal evolusi Swift untuk informasi lebih lanjut tentang fitur ini.