英文:
Compiler Error Passing `associatedtype` value from one Protocol to another
问题
protocol Valuable {
associatedtype ValueType
var value: ValueType? { get }
var rules: [AnyValidatable<Self>] { get }
mutating func update(_ newValue: ValueType) throws
}
protocol Validatable {
associatedtype T
func validate(_ value: T) throws
}
struct AnyValidatable<V>: Validatable {
typealias T = V.ValueType
private let _validate: (T) throws -> Void
init<U: Validatable>(_ validatable: U) where U.T == T {
_validate = validatable.validate
}
func validate(_ value: T) throws {
try _validate(value)
}
}
struct StringValue: Valuable {
typealias ValueType = String
var value: String?
var rules: [AnyValidatable<StringValue>]
mutating func update(_ newValue: String) throws {
let errors = rules.compactMap { rule in
do {
try rule.validate(newValue)
return nil
} catch {
return error
}
}
if errors.isEmpty {
self.value = newValue
} else {
throw ValidationError.Invalid(subErrors: errors)
}
}
}
英文:
I'm working in Swift 5.7. I need to implement what's essentially form validation but with a twist. Instead of evaluating rules and returning boolean values, I need to throw an error upon failure.
I have the following protocol, which can be added to but otherwise not changed:
protocol Valuable {
associatedtype ValueType
var value: ValueType? { get }
mutating func update(_ newValue: ValueType) throws
}
I'm attempting to add an array of rules, conforming to a different protocol, which need to pass in order for the new value to be saved. I have two problems which I'm facing:
- I need to constrain the second protocol to have the same associated type as
Valuable
- I'm getting a compiler error when I attempt to pass
value
as a parameter to a function in the second protocol:Member 'validate' cannot be used on a value of type 'any Validatable'; consider using a generic constraint instead
My understanding of this compiler error (which may be a faulty understanding) is that because any Validatable
is being consumed, Swift is unable to unbox and/or infer the underlying type. How do I get around this?
For enforcing the same associatedtype
, I tried adding associatedtype Rule: Validatable where Rule.T == ValueType
to the Valuable
protocol, but then the compiler complains that my struct does not conform to Valuable
. I looked at other posts regarding constraining protocol types, but it appears that those recommendations either no longer work with Swift 5.7, or are missing something.
Here's a simplified example of my code:
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)
}
}
}
答案1
得分: 1
Add a primary associate type to Validatable
:
protocol Validatable<T> {
associatedtype T
func validate(_ value: T) throws
}
so that you can easily specify that "Validatable
where T
is ValueType
" should be the rules
array element type.
// in Valuable
var rules: [any Validatable<ValueType>] { get }
// in StringValue
var rules: [any Validatable<String>]
The compiler also seems to be unable to infer the result type of compactMap
, so you should help it out:
let errors: [Error] = rules.compactMap { rule in
^^^^^^^^^
Also consider adding a primary associated for Valuable
too. Judging from the protocol names, this seems appropriate and probably will be useful later.
protocol Valuable<ValueType> {
英文:
Add a primary associate type to Validatable
:
protocol Validatable<T> { // add a <T> here
associatedtype T
func validate(_ value: T) throws
}
so that you can easily specify that "Validatable
where T
is ValueType
" should be the rules
array element type.
// in Valuable
var rules: [any Validatable<ValueType>] { get }
// in StringValue
var rules: [any Validatable<String>]
The compiler also seems to be unable to infer the result type of compactMap
, so you should help it out:
let errors: [Error] = rules.compactMap { rule in
^^^^^^^^^
Also consider adding a primary associated for Valuable
too. Judging from the protocol names, this seems appropriate and probably will be useful later.
protocol Valuable<ValueType> {
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论