Robert Atkins
Robert Atkins

Reputation: 24718

How do I call a static property on a Swift protocol from a generic function?

Given this code:

public protocol Selectable {

    typealias T

    var selected: Bool { get }

    static var defaultValue: T { get }

}

public func selected<T: Selectable>(items: [T]) -> T {
    if let selected = items.filter({$0.selected}).first {
        return selected
    }
    return T.defaultValue
}

I get an error on the return line: "Cannot convert return expression of type 'T.T' to expected return type 'T'".

Changing it to return T.defaultValue as! T seems to work but that doesn't make any sense to me. Am I missing something or should I file a radar?

Upvotes: 6

Views: 3586

Answers (2)

Robert Atkins
Robert Atkins

Reputation: 24718

Building on @rintaro's answer, using Self for the type of defaultValue means the typealias is unnecessary:

public protocol Selectable {

    var selected: Bool { get }

    static var defaultValue: Self { get }

}

public func selected<T: Selectable >(items: [T]) -> T {
    if let selected = items.filter({$0.selected}).first {
        return selected
    }
    return T.defaultValue
}

(I found this as changing defaultValue's type to Self made my implementing class not conform to the protocol anymore—and I noticed I wasn't even referring to the typealias Value; removing that made my implementing class comply again.)

Upvotes: 1

rintaro
rintaro

Reputation: 51911

You can use Self in the protocol:

public protocol Selectable {

    var selected: Bool { get }

    static var defaultValue: Self { get }
    //                       ^^^^
}

public func selected<T: Selectable>(items: [T]) -> T {
    if let selected = items.filter({$0.selected}).first {
        return selected
    }
    return T.defaultValue
}

OR, if you want to use typealias, you have to:

public protocol Selectable {
    typealias Value

    var selected: Bool { get }

    static var defaultValue: Value { get }
}

public func selected<T: Selectable where T.Value == T>(items: [T]) -> T {
    //                             ^^^^^^^^^^^^^^^^^^
    if let selected = items.filter({$0.selected}).first {
        return selected
    }
    return T.defaultValue
}

Upvotes: 7

Related Questions