绕过"协议X只能用作通用约束"

时间:2016-11-04 00:49:12

标签: swift

我正在处理表单输入库。我的目标是拥有一组可重用的验证器,可以应用于一组表单字段。我遇到了专门针对我的通用协议的困难。以下代码中的完整错误为protocol 'FieldValidator' can only be used as a generic constraint because it has Self or associated type requirements

完成游乐场准备代码:

import Foundation

protocol FieldValidator {
  associatedtype InputType: Any
  func validate(input value: InputType)
}

struct EmailValidator: FieldValidator {
  func validate(input value: String) {}
}

enum Field {
  case string(_: [FieldValidator])
  case integer(_: [FieldValidator])
}

let emailField: Field = .string([EmailValidator()])

我尝试了什么

我理解,在Field枚举中,我无法引入FieldValidator,因为它需要知道它需要的InputType验证器。我希望我能以某种方式告诉它,也许是这样的:

case string(_: [FieldValidator<String>])
case integer(_: [FieldValidator<Int>])

或者这个:

case string(_: [FieldValidator where InputType == String])
case integer(_: [FieldValidator where InputType == Int])

但这些都不起作用。有没有办法保持这种架构?

使用struct代替enum进行字段类型修改:

struct StringField {
  typealias InputType = String
  let validators: [FieldValidator]
}

我似乎仍然在定义验证器集时遇到同样的问题(必须在初始化字段时提供验证器):protocol 'FieldValidator' can only be used as a generic constraint because it has Self or associated type requirements

1 个答案:

答案 0 :(得分:1)

  

我想我想要做的是提供一种机制,通过这种机制,某人可以定义一个Field,定义它所拥有的值的类型,并定义一组可重用的Validator,它们将对该值进行操作并确定是否它是有效的

你可能会追求这样的事情;它是愚蠢但有效的,特别是如果没有很多字段类型:

protocol FieldValidator {
    associatedtype T
    func validate(input:T)
}

class StringValidator : FieldValidator {
    func validate(input:String) { fatalError("must override me") }
}

class IntValidator : FieldValidator {
    func validate(input:Int) { fatalError("must override me") }
}

class ActualStringValidator : StringValidator {
    override func validate(input:String) { print(input)}
}

enum Field {
    case string([StringValidator])
    case int([IntValidator])
}

正如您所看到的,我只是使用类层次结构来解决问题(因此我们不必进行类型擦除)。特别是,现在说:

是合法的
let f = Field.string([ActualStringValidator()])

以下是如何测试它:

let f = Field.string([ActualStringValidator()])
if case Field.string(let arr) = f {
    for thing in arr {
        thing.validate(input:"howdy")
    }
}