0

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

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是行不通的。

有没有办法限制关联类型?

4

2 回答 2

1

Transformer您可以为协议创建扩展

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

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

StringToInt().convert()

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

struct DoubleToFloat: Transformer {
    func transform(s: Double) -> Float {
        return Float(s)
    }
}

DoubleToFloat().convert() // compiler error
于 2016-01-10T12:58:06.450 回答
0

摆脱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
    }
}
于 2016-01-10T14:07:37.713 回答