我正在处理表单输入库。我的目标是拥有一组可重用的验证器,可以应用于一组表单字段。我遇到了专门针对我的通用协议的困难。以下代码中的完整错误为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
。
答案 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")
}
}