Так я объяснял непрозрачные типы своей команде.
Ваша типичная общая функция, например фрагмент, которым вы поделились, может показаться понятной. Почему следующий код не будет интерпретирован компилятором как возвращающий Array<T>
?
// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
Причина ошибки заключается в том, как C
используется в качестве возврата. Когда вы видите что-то в форме -> C
в качестве универсального возврата, вы оставляете определение этого типа до вызывающей стороны .
Если предположить, что Array
соответствует Container
, то вы сможете сделать что-то вроде этого:
let array: Array<Int> = makeProtocolContainer(Int(5))
Ваш сайт вызовов определяет, какой тип C
is.
Но что если вы хотите, чтобы сама функция определяла это? Вот тут и появляются непрозрачные типы.
В приведенном выше примере ожидается, что функция makeProtocolContainer
будет возвращать нечто общее, что может определить вызывающая сторона. Но сама функция определяет конкретную реализацию, которая соответствует C, что не то же самое, что возвращение универсального типа.
Если вы хотите, чтобы сама функция указывала тип, то вам просто нужно изменить его с помощьюновое ключевое слово some
в Swift.
func makeProtocolContainer<T>(item: T) -> some Container {
return [item]
}
Теперь функция может возвращать конкретный тип. В результате вызывающая сторона может знать его только как этот непрозрачный тип, даже если они могут быть равны.
/// Error: Cannot convert value of type 'some Container' to specified type 'Array<String>'
let container: Array<String> = makeProtocolContainer(item: "A")
Вы также не можете использовать универсальный тип для хранения непрозрачного типа.
/// Error: Protocol 'Container' can only be used as a generic constraint because it has Self or associated type requirements
let container: Container = makeProtocolContainer(item: 5)
Но вы можете либо разрешить вывод типа, либо указать непрозрачный тип самостоятельно.
let container = makeProtocolContainer(item: 5)
// Same as
let container: some Container = makeProtocolContainer(item: 5)