Я думаю, что ключом к пониманию того, что здесь происходит, является различие между вещами, которые динамически определяются во время выполнения, и вещами, которые статически определяются во время компиляции. Это не помогает, что на большинстве языков, таких как Java, протоколы (или интерфейсы) связаны с получением полиморфного поведения при времени выполнения, тогда как в Swift протоколы со связанными типами также используются для получения полиморфного поведения при времени компиляции ,
Всякий раз, когда вы видите общий заполнитель, например T
в вашем примере, какой тип заполняется для этого T
, определяется во время компиляции. Таким образом, в вашем примере:
func returnsSomethingWithAwesomeness<T: HasAwesomeness>(key: String) -> T
говорит: returnsSomethingWithAwesomeness
это функция, которая может работать на любом типе T
, только до тех пор, как T
соответствует HasAwesomeness
.
Но заполняется для T
определяется в точке returnsSomethingWithAwesomeness
называется - Swift будет смотреть на всю информацию на месте вызова и решить, какой тип T
есть, и заменить все T
заполнителей с этим типом.*
Итак, пусть на месте вызова выбора в том, что T
является String
, вы можете думать о returnsSomethingWithAwesomeness
, как переписываются со всеми вхождениями заполнителя T
заменен String
:
// giving the type of s here fixes T as a String
let s: String = returnsSomethingWithAwesomeness("bar")
func returnsSomethingWithAwesomeness(key: String) -> String {
if key == "foo" {
return "Amazing Foo"
}
else {
return 42
}
}
Примечания, T
является заменены на String
и не с типом HasAwesomeness
. HasAwesomeness
используется только как ограничение, то есть ограничение возможных типов T
.
Когда вы смотрите на это так, вы можете видеть, что return 42
в else
не имеет смысла - как бы вы могли вернуть 42 из функции, которая возвращает строку?
Чтобы убедиться, что returnsSomethingWithAwesomeness
может работать с любым T
, Swift ограничивает использование только тех функций, которые гарантированно будут доступны из заданных ограничений. В этом случае все, что мы знаем о T
, состоит в том, что оно соответствует HasAwesomeness
. Это означает, что вы можете вызвать метод returnsSomethingWithAwesomeness
на любом T
или использовать его с другой функцией, которая сдерживает тип до HasAwesomeness
или назначает одну переменную типа T
другой (назначение поддержки всех типов) и , то есть.
Вы не можете сравнить это с другими Ц (без гарантии, что он поддерживает ==
). Вы не можете создавать новые (кто знает, будет ли у T
соответствующий метод инициализации?). И вы не можете создать его из строкового или целочисленного литерала (для этого потребуется T
, чтобы соответствовать либо StringLiteralConvertible
, либо IntegerLiteralConvertible
, что не обязательно - следовательно, эти две ошибки при попытке создать тип с использованием одного из этих типов литералов).
Можно написать общие функции, которые возвращают общий тип, который соответствует протоколу. Но то, что будет возвращено, будет конкретным типом, а не протоколом, поэтому тип не будет определяться динамически. Например:
func returnCollectionContainingOne<C: ExtensibleCollectionType where C.Generator.Element == Int>() -> C {
// this is allowed because the ExtensibleCollectionType procol
// requires the type implement an init() that takes no parameters
var result = C()
// and it also defines an `append` function that allows you to do this:
result.append(1)
// note, the reason it was possible to give a "1" as the argument to
// append was because of the "where C.Generator.Element == Int" part
// of the generic placeholder constraint
return result
}
// now you can use returnCollectionContainingOne with arrays:
let a: [Int] = returnCollectionContainingOne()
// or with ContiguousArrays:
let b: ContiguousArray = returnCollectionContainingOne()
Придумайте returnCollectionContainingOne
в этом коде, как на самом деле быть две функции, одна реализована для ContiguousArray
, и один для Array
, написанной автоматически компилятором в точке, которую вы называете их (и, следовательно, где он может исправить C
- особый тип). Не одна функция, которая возвращает протокол, а две функции, возвращающие два разных типа. Точно так же returnsSomethingWithAwesomeness
не может вернуть String
или Int
во время выполнения на основе какого-либо динамического аргумента, вы не могли бы написать версию returnCollectionContainingOne
, которая возвращала массив или смежный массив. Все, что он может вернуть, это T
, и во время компиляции все, что T
действительно может быть заполнено компилятором.
* Это небольшое упрощение того, что на самом деле делает компилятор, но это будет сделано для этого объяснения.
Любой может помочь объяснить, почему в Swift 3 следующий код имеет схожую ошибку? func paths() -> T? { return [Int]() } –
Mingming