Ограничить связанный тип
Упрощенная версия моего кода, которая показывает проблему:
protocol Transformer {
typealias Input
typealias Output
func transform(s: Input) -> Output
}
protocol InputType {}
protocol OutputType {}
extension Int: OutputType {}
extension String: InputType {}
struct StringToInt: Transformer {
typealias Input = String
typealias Output = Int
func transform(s: Input) -> Output {
return s.characters.count
}
}
typealias Transform = InputType -> OutputType
func convert<T: Transformer where T.Input == InputType, T.Output == OutputType>
(transformer: T) -> Transform {
return transformer.transform
}
convert(StringToInt()) // error: Cannot invoke 'convert' with an argument list of type '(StringToInt)'
Я предполагаю, что ошибка происходит, потому что компилятор не может добраться до StringToInt
и убедитесь, что Input
а также Output
действительно соответствуют InputType
а также OutputType
соответственно.
Для меня лучший способ решить эту проблему - ограничить связанные типы непосредственно в протоколе. Это было бы более выразительно, и у компилятора было бы больше информации. Но просто делаю typealias Input: InputType
не работает
Есть ли способ ограничить связанный тип?
2 ответа
Вы можете создать расширение для Transformer
протокол
extension Transformer where Input: InputType, Output: OutputType {
func convert() -> Input -> Output {
return transform
}
}
Теперь вы можете позвонить convert
метод на StringToInt
пример.
StringToInt().convert()
Но для другого типа, который не имеет Input
а также Output
принятие InputType
а также OutputType
это не скомпилируется
struct DoubleToFloat: Transformer {
func transform(s: Double) -> Float {
return Float(s)
}
}
DoubleToFloat().convert() // compiler error
Избавиться от Transform
использование псевдонима, добавьте обобщенные значения для типов ввода и вывода и сопоставьте их с Transformer
и вы получите рабочий код для StringToInt
и для других трансформаторов, которые вы пишете:
func convert<T: Transformer, I, O where T.Input == I, T.Output == O>
(transformer: T) -> I -> O {
return transformer.transform
}
convert(StringToInt())
Кстати, вам не нужно указывать псевдонимы типа для StringToInt
компилятор может вывести их из определения функции, если вы укажете фактические типы:
struct StringToInt: Transformer {
func transform(s: String) -> Int {
return s.characters.count
}
}