Home > Enterprise >  Swift add constraint extension to Protocol that has an associated type
Swift add constraint extension to Protocol that has an associated type

Time:11-02

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.

  • Related