some View
เป็นประเภทผลลัพธ์ทึบแสงตามที่แนะนำโดยSE-0244และมีให้ใน Swift 5.1 พร้อม Xcode 11 คุณสามารถคิดว่านี่เป็นตัวยึดตำแหน่งทั่วไป "ย้อนกลับ"
ไม่เหมือนกับตัวยึดตำแหน่งทั่วไปที่ผู้โทรพอใจ:
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.
ประเภทผลลัพธ์ทึบแสงเป็นตัวยึดตำแหน่งโดยนัยที่พอใจโดยการนำไปใช้ดังนั้นคุณสามารถคิดถึงสิ่งนี้:
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
เหมือนอย่างนี้
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
ในความเป็นจริงในที่สุดเป้าหมายที่มีคุณลักษณะนี้คือการให้ยาชื่อสามัญกลับในรูปแบบที่ชัดเจนมากขึ้นนี้ซึ่งก็จะช่วยให้คุณเพิ่มข้อ จำกัด -> <T : Collection> T where T.Element == Int
เช่น เห็นโพสต์นี้สำหรับข้อมูลเพิ่มเติม
สิ่งสำคัญที่ต้องใช้เวลาห่างจากนี้ก็คือฟังก์ชั่นที่กลับมาsome P
เป็นหนึ่งที่ส่งกลับค่าที่เฉพาะเจาะจงเดียว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()
}
}
เนื่องจากตัวยึดตำแหน่งทั่วไปโดยนัยไม่สามารถทำได้หลายประเภท
สิ่งนี้ตรงกันข้ามกับฟังก์ชันที่ส่งคืนP
ซึ่งสามารถใช้เพื่อเป็นตัวแทนของทั้งสอง S1
และS2
เนื่องจากเป็นตัวแทนของP
ค่าที่สอดคล้องกันโดยพลการ:
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
เอาล่ะเพื่อให้สิ่งที่เป็นประโยชน์ทำผลชนิดทึบแสง-> some P
ได้มากกว่าประเภทโปรโตคอลการกลับมา-> P
?
1. ประเภทผลลัพธ์แบบทึบสามารถใช้กับ PAT ได้
ข้อ จำกัด ในปัจจุบันที่สำคัญของโปรโตคอลคือ PATs (โปรโตคอลที่มีประเภทที่เกี่ยวข้อง) ไม่สามารถใช้เป็นประเภทจริง แม้ว่านี่จะเป็นข้อ จำกัด ที่มีแนวโน้มว่าจะยกระดับขึ้นในภาษาในอนาคตเนื่องจากประเภทผลลัพธ์ที่ทึบแสงเป็นเพียงตัวยึดตำแหน่งทั่วไป แต่ก็สามารถใช้กับ PAT ได้ในปัจจุบัน
หมายความว่าคุณสามารถทำสิ่งต่าง ๆ เช่น:
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2. ประเภทผลลัพธ์แบบทึบมีตัวตน
เนื่องจากชนิดผลลัพธ์ทึบแสงบังคับให้ส่งคืนชนิดคอนกรีตเดียวคอมไพเลอร์รู้ว่าการเรียกสองครั้งไปยังฟังก์ชันเดียวกันต้องส่งคืนสองค่าที่เป็นประเภทเดียวกัน
หมายความว่าคุณสามารถทำสิ่งต่าง ๆ เช่น:
// 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.
สิ่งนี้ถูกกฎหมายเพราะคอมไพเลอร์รู้ว่าทั้งสองx
และy
มีรูปธรรมเหมือนกัน นี่คือความต้องการที่สำคัญสำหรับที่ทั้งพารามิเตอร์ของประเภท==
Self
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
ซึ่งหมายความว่าคาดว่าจะมีค่าสองค่าที่มีทั้งประเภทเดียวกันกับประเภทที่เป็นรูปธรรม แม้ว่าEquatable
จะเป็นประเภทที่ใช้งานได้คุณจะไม่สามารถเปรียบเทียบEquatable
ค่าที่สอดคล้องกันสองค่ากับตัวอย่างเช่น:
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.
ในขณะที่คอมไพเลอร์ไม่สามารถพิสูจน์ได้ว่าสองEquatable
ค่าโดยพลการมีชนิดคอนกรีตต้นแบบเดียวกัน
ในทำนองเดียวกันถ้าเราแนะนำฟังก์ชันส่งคืนชนิดทึบแสงอื่น:
// 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.
ตัวอย่างกลายเป็นสิ่งผิดกฎหมายเพราะถึงแม้ว่าทั้งสองfoo
และbar
กลับมาsome Equatable
ตัวยึดตำแหน่งทั่วไป "ย้อนกลับ" ของพวกเขาOutput1
และOutput2
อาจได้รับความพึงพอใจตามประเภทที่แตกต่างกัน
3. ประเภทผลทึบแสงประกอบด้วยตัวยึดตำแหน่งทั่วไป
ซึ่งแตกต่างจากค่าที่พิมพ์โปรโตคอลปกติประเภทผลทึบแสงประกอบได้ดีกับตัวยึดตำแหน่งทั่วไปตัวอย่างเช่น
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.
สิ่งนี้จะไม่ทำงานหากmakeP
เพิ่งส่งคืนP
เนื่องจากP
ค่าสองค่าอาจมีประเภทคอนกรีตต้นแบบที่แตกต่างกันตัวอย่างเช่น:
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.
เหตุใดจึงใช้ประเภทผลลัพธ์ทึบแสงเหนือประเภทคอนกรีต
ณ จุดนี้คุณอาจคิดกับตัวเองทำไมไม่เพียงแค่เขียนรหัสเป็น:
func makeP() -> S {
return S(i: 0)
}
การใช้ชนิดผลลัพธ์แบบทึบนั้นช่วยให้คุณสร้างS
รายละเอียดการนำไปปฏิบัติได้โดยการเปิดเผยเฉพาะอินเตอร์เฟสที่ให้ไว้โดยP
ให้ความยืดหยุ่นในการเปลี่ยนประเภทคอนกรีตในภายหลังโดยไม่ต้องทำลายโค้ดใด ๆ ขึ้นอยู่กับฟังก์ชัน
ตัวอย่างเช่นคุณสามารถแทนที่:
func makeP() -> some P {
return S(i: 0)
}
ด้วย:
func makeP() -> some P {
return T(i: 1)
}
โดยไม่ทำลายรหัสใด ๆ makeP()
ที่โทร
ดูส่วน Opaque Typesของคู่มือภาษาและข้อเสนอวิวัฒนาการ Swiftสำหรับข้อมูลเพิ่มเติมเกี่ยวกับคุณสมบัตินี้