编译器错误将 `associatedtype` 值从一个协议传递到另一个协议

问题描述 投票:0回答:1

我正在使用 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)
        }
    }
}
swift swift-protocols
1个回答
0
投票

将主要关联类型添加到

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> {
© www.soinside.com 2019 - 2024. All rights reserved.