Почему компилятор Swift не может определить тип возвращаемого протокола со связанным элементом? - PullRequest
1 голос
/ 01 ноября 2019

При чтении документации по Непрозрачные типы в Руководстве по языку Swift, почти в самом конце находится фрагмент:

// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
    return [item]
}

Container имеет связанный тип, Itemи Array был расширен для соответствия Container. Почему компилятору не хватает информации, чтобы сделать вывод, что это Array<T>?

Другой сбивающий с толку момент заключается в том, что с универсальными функциями они написаны, как правило, для использования с любыми типами, указанными клиентским кодом. Так что, не должен ли клиентский код диктовать, что такое C вместо функции, навязывающей свой тип возврата Array?

1 Ответ

1 голос
/ 01 ноября 2019

Так я объяснял непрозрачные типы своей команде.

Ваша типичная общая функция, например фрагмент, которым вы поделились, может показаться понятной. Почему следующий код не будет интерпретирован компилятором как возвращающий 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)
...