some View
là loại kết quả mờ đục như được giới thiệu bởi SE-0244 và có sẵn trong Swift 5.1 với Xcode 11. Bạn có thể nghĩ đây là một trình giữ chỗ chung "ngược".
Không giống như một trình giữ chỗ chung thông thường được người gọi hài lòng:
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.
Một loại kết quả mờ đục là một giữ chỗ chung chung được thỏa mãn bởi việc triển khai , vì vậy bạn có thể nghĩ về điều này:
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
như thế này:
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
Trên thực tế, mục tiêu cuối cùng với tính năng này là cho phép các tổng quát ngược ở dạng rõ ràng hơn này, điều này cũng sẽ cho phép bạn thêm các ràng buộc, ví dụ -> <T : Collection> T where T.Element == Int
. Xem bài đăng này để biết thêm .
Điều quan trọng để lấy đi từ này là một hàm trả về some P
là một trong đó trả về một giá trị của một cụ đơn kiểu dữ liệu cụ để tuân thủ P
. Cố gắng trả về các loại tuân thủ khác nhau trong hàm dẫn đến lỗi trình biên dịch:
// 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()
}
}
Vì giữ chỗ chung ẩn có thể được thỏa mãn bởi nhiều loại.
Điều này trái ngược với một hàm trả về P
, có thể được sử dụng để biểu diễn cả hai S1
và S2
bởi vì nó đại diện cho một P
giá trị tuân thủ tùy ý :
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Được rồi, vì vậy những lợi ích nào làm cho loại kết quả mờ -> some P
có các loại trả về giao thức-> P
?
1. Các loại kết quả mờ có thể được sử dụng với PAT
Một hạn chế chính hiện tại của các giao thức là các PAT (giao thức với các loại liên quan) không thể được sử dụng như các loại thực tế. Mặc dù đây là một hạn chế có thể sẽ được gỡ bỏ trong phiên bản tương lai của ngôn ngữ, bởi vì các loại kết quả mờ đục thực sự chỉ là các trình giữ chỗ chung chung, chúng có thể được sử dụng với các PAT ngày hôm nay.
Điều này có nghĩa là bạn có thể làm những việc như:
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2. Các loại kết quả mờ có bản sắc
Vì các loại kết quả mờ thực thi một loại bê tông duy nhất được trả về, trình biên dịch biết rằng hai lệnh gọi đến cùng một hàm phải trả về hai giá trị cùng loại.
Điều này có nghĩa là bạn có thể làm những việc như:
// 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.
Điều này là hợp pháp vì trình biên dịch biết rằng cả hai x
và y
có cùng loại cụ thể. Đây là một yêu cầu quan trọng đối với ==
, trong đó cả hai tham số của loại Self
.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
Điều này có nghĩa là nó mong đợi hai giá trị là cùng loại với loại phù hợp cụ thể. Ngay cả khi Equatable
có thể sử dụng như một loại, bạn sẽ không thể so sánh hai Equatable
giá trị tuân thủ tùy ý với nhau, ví dụ:
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.
Vì trình biên dịch không thể chứng minh rằng hai Equatable
giá trị tùy ý có cùng loại bê tông cơ bản.
Theo cách tương tự, nếu chúng tôi giới thiệu một hàm trả về loại mờ khác:
// 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.
Ví dụ trở nên bất hợp pháp vì mặc dù cả hai foo
và bar
trả về some Equatable
, các trình giữ chỗ chung "ngược" của chúng Output1
và Output2
có thể được thỏa mãn bởi các loại khác nhau.
3. Các loại kết quả mờ kết hợp với giữ chỗ chung
Không giống như các giá trị gõ giao thức thông thường, các loại kết quả mờ kết hợp tốt với các trình giữ chỗ chung thông thường, ví dụ:
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.
Điều này sẽ không hoạt động nếu makeP
vừa trả về P
, vì hai P
giá trị có thể có các loại cụ thể cơ bản khác nhau, ví dụ:
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.
Tại sao sử dụng loại kết quả mờ trên loại bê tông?
Tại thời điểm này, bạn có thể nghĩ cho chính mình, tại sao không viết mã như sau:
func makeP() -> S {
return S(i: 0)
}
Chà, việc sử dụng loại kết quả mờ cho phép bạn tạo loại S
chi tiết triển khai bằng cách chỉ hiển thị giao diện được cung cấp bởiP
, cho phép bạn linh hoạt thay đổi loại cụ thể sau khi xuống dòng mà không phá vỡ bất kỳ mã nào phụ thuộc vào chức năng.
Ví dụ: bạn có thể thay thế:
func makeP() -> some P {
return S(i: 0)
}
với:
func makeP() -> some P {
return T(i: 1)
}
mà không phá vỡ bất kỳ mã nào gọi makeP()
.
Xem phần Opaque Type của hướng dẫn ngôn ngữ và đề xuất tiến hóa Swift để biết thêm thông tin về tính năng này.