When I add a constraint extension to a protocol that has an associated type, the swift compiler ignores my constraint.
When I write:
protocol Arr {
associatedtype Element
func node(_ at: Int) -> Element?
}
extension Arr where Element == String {
func node(_ at: Int) -> String? {
nil
}
}
struct Doo: Arr {
}
Xcode builds successfully and it thinks my Doo
's Element
is String
. It ignores the where Element == String
constraint.
When I write:
protocol Arr {
associatedtype Element
func node(_ at: Int) -> Element?
}
extension Arr where Element == String {
func node(_ at: Int) -> Element? { // use Element
nil
}
}
struct Doo: Arr {
}
Xcode shows an error, as expected.
Is this an Xcode bug or a Swift feature?
Xcode version: Version 13.1 (13A1030d)
Swift version:
swift-driver version: 1.26.9 Apple Swift version 5.5.1 (swiftlang-1300.0.31.4 clang-1300.0.29.6)
Target: arm64-apple-macosx12.0
CodePudding user response:
The errors in second case arises because Xcode is not able to infer the type of Element. If you specify it, everything compiles.
struct Doo: Arr {
typealias Element = String
}
CodePudding user response:
What happens here is that Swift will gladly satisfy protocol requirements, if it can extract them from the context.
For example, assuming the extension would not exist, and the Doo
definition would be like this:
struct Doo: Arr {
func node(_ at: Int) -> String? {
nil
}
}
, the the Swift compiler will happily fill the Element
associated type.
A similar thing happens with your first snippet of code, Doo
conforms to Arr
, and the compiler finds a definition that satisfies all protocol requirements. Swift doesn't ignore the Element == String
constraint, because it associates it with the Doo
struct.
If you would add a second extension in a similar fashion, but for another type (an Int
for example), you'll see that you receive the expected error. This happens because the compiler can no longer infer the protocol requirements.
The Swift compiler eagerly infers as much as possible from the context it can reach to, most of the time gives great results, sometimes not so great (especially when working with closures), and sometimes it gives unexpected results (like this one).
If you want to be sure that the compiler infers the types you want, a solution would be to explicitly declare all involved types.