我正在使用 Swift 5.7。我需要实现本质上是表单验证的东西,但要有所不同。我需要在失败时抛出错误,而不是评估规则和返回布尔值。
我有以下协议,可以添加但不能更改:
protocol Valuable {
associatedtype ValueType
var value: ValueType? { get }
mutating func update(_ newValue: ValueType) throws
}
我正在尝试添加一组符合不同协议的规则,这些规则需要通过才能保存新值。我面临两个问题:
Valuable
value
作为参数传递给第二个协议中的函数时出现编译器错误:Member 'validate' cannot be used on a value of type 'any Validatable'; consider using a generic constraint instead
我对这个编译器错误的理解(这可能是一个错误的理解)是因为
any Validatable
正在被消耗,Swift 无法拆箱和/或推断底层类型。我该如何解决这个问题?
为了强制执行相同的
associatedtype
,我尝试将associatedtype Rule: Validatable where Rule.T == ValueType
添加到Valuable
协议,但随后编译器抱怨我的结构不符合Valuable
。我查看了有关约束协议类型的其他帖子,但这些建议似乎不再适用于 Swift 5.7,或者缺少某些内容。
这是我的代码的一个简化示例:
enum ValidationError: Error {
case Invalid(subErrors: [Error])
}
protocol Valuable {
associatedtype ValueType
var value: ValueType? { get }
var rules: [any Validatable] { get }
mutating func update(_ newValue: ValueType) throws
}
protocol Validatable {
associatedtype T
func validate(_ value: T) throws
}
struct StringValue: Valuable {
typealias ValueType = String
var value: String?
var rules: [any Validatable]
mutating func update(_ newValue: String) throws {
let errors = rules.compactMap { rule in
do {
try rule.validate(newValue) // <~~ Compiler error here
return nil
} catch {
return error
}
}
if errors.isEmpty {
self.value = newValue
} else {
throw ValidationError.Invalid(subErrors: errors)
}
}
}
将主要关联类型添加到
Validatable
:
protocol Validatable<T> { // add a <T> here
associatedtype T
func validate(_ value: T) throws
}
以便您可以轻松指定“
Validatable
where T
is ValueType
”应该是rules
数组元素类型。
// in Valuable
var rules: [any Validatable<ValueType>] { get }
// in StringValue
var rules: [any Validatable<String>]
编译器似乎也无法推断
compactMap
的结果类型,所以你应该帮助它:
let errors: [Error] = rules.compactMap { rule in
^^^^^^^^^
还考虑为
Valuable
添加一个主要关联。从协议名称来看,这似乎很合适,以后可能会有用。
protocol Valuable<ValueType> {