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
, возвращает значение определенного одного * 1026.* конкретный тип, который соответствует 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
Основное ограничение протоколов в настоящее время заключается в том, что PAT (протоколы со связанными типами) не могут использоваться в качестве фактических типов.Хотя это ограничение, скорее всего, будет снято в будущей версии языка, поскольку непрозрачные типы результатов являются просто общими заполнителями, их можно использовать сегодня с 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
return 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()
.
См. раздел «Непрозрачные типы» руководства по языкам и предложение Swift Evolution для получения дополнительной информации об этой функции.