Как вызвать неоднозначную универсальную функцию в Swift?
Я определил две общие функции
func job<T: Comparable>(x: T) {
println("1")
}
func job<T: Hashable>(x: T) {
println("2")
}
И когда я пытаюсь вызвать один из них, например:
let myInt: Int = 1 // Explicit Int just for clarity of the example
job(myInt)
Конечно Свифт жалуется и выдает ошибку
неоднозначное использование слова "работа"
это понятно, потому что не ясно, хочу ли я использовать сопоставимый один или Хэшируемый (Int соответствует им обоим)
Есть ли способ подсказать компилятору, какой из них я хочу использовать?
1 ответ:
Это неоднозначно, потому что
Intявляется одновременноHashableиComparable, и ни один из этих двух протоколов не находится в одной иерархии. (Вы можете просмотретьIntиерархия протоколов на Swifter .)func f<T: Hashable>(t: T) { println("Hashable: \(t)") } func f<T: Comparable>(t: T) { println("Comparable: \(t)") } let number = 5 f(number) // error: ambiguous use of 'f'Вы не можете явно указать ему, какую функцию вызывать, из-за связанных требований к типу каждого протокола, но то, что вы можете сделать, это определить третью функцию:
func f<T: Comparable where T: Hashable>(t: T) { println("Both Hashable & Comparable: \(t)") } f(number) // Both Hashable & Comparable: 5Именно так Swift реализует оператор
..<, который в противном случае был бы неоднозначно для типов, реализующих какComparable, так иForwardIndexType.
Чтобы расширить немного дальше, вот взгляд на то, что я подразумевал под "вы не можете явно сказать ему, какую функцию вызывать, из-за связанных требований к типу каждого протокола."Протоколы могут использоваться как типы, как описано в главе Swift book о Протоколах :
protocol RandomNumberGenerator { func random() -> Double } class Dice { let generator: RandomNumberGenerator // ... }В этом примере свойство генератора может быть любого типа, соответствующего
RandomNumberGenerator- аналогично тому, какid<ProtocolName>используется в Однако протоколы могут использоваться в качестве типов только, если они не включают в свое объявление ассоциированный тип или ссылкуSelf. К сожалению, это исключает почти все встроенные типы в Swift, включаяHashableиComparable.
Hashableнаследуется отEquatable, который ссылается наSelfпри определении оператора==:func ==(lhs: Self, rhs: Self) -> BoolИ
Comparableделает то же самое со своими операторами:func <=(lhs: Self, rhs: Self) -> Bool // similar definitions for <, >, and >=Эти протоколы могут только использоваться как общие ограничения, и не используется в качестве типа при объявлении переменной. (Насколько я могу судить, это недокументировано, но обнаруживается через сообщения об ошибках.)
Два протокола, которые не имеют это ограничение, являютсяPrintableиBooleanType, поэтому мы можем посмотреть, как они работают.Boolявляется единственным встроенным типом, который соответствуетBooleanType, и это такжеPrintable, так что это будет наш тестовый тип. Вот наши общие функцииp()и переменнаяt- обратите внимание, что, как и раньше, мы не можем просто вызвать функция сt:func p<T: Printable>(t: T) { println("Printable: \(t)") } func p<T: BooleanType>(t: T) { println("BooleanType: \(t)") } let t: Bool = true p(t) // error: Ambiguous use of 'p'Вместо этого нам нужно бросить (upcast?)
tк определенному протоколу, используя ключевое словоas, и вызываем определенную универсальную функцию таким образом:Таким образом, пока у нас есть квалифицирующий протокол, мы можем выбрать, какой вариант универсального метода вызывать.p(t as Printable) // Printable: true p(t as BooleanType) // BooleanType: true