Как вызвать неоднозначную универсальную функцию в 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