约束关联类型

Constrain an associated type

显示问题的我的代码的简化版本:

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 并验证 InputOutput 确实符合 InputTypeOutputType分别。

对我来说,解决这个问题的最好方法是直接在协议中约束关联类型。它将更具表现力,编译器将获得更多信息。但是简单地做 typealias Input: InputType 是行不通的。

有什么方法可以约束关联类型吗?

您可以为 Transformer 协议创建扩展

extension Transformer where Input: InputType, Output: OutputType {
    func convert() -> Input -> Output {
        return transform
    }
}

现在您可以在 StringToInt 个实例上调用 convert 方法。

StringToInt().convert()

但是对于其他没有InputOutput的类型采用InputTypeOutputType它不会编译

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
    }
}