Protocols with default implementations cause ambiguity

I guess you just can't use protocol defaults in this situation. I'm trying to make it easy for enums that are RawRepresentable to conform to ColumnMappable.

protocol ColumnMappable {
    static var typeStr: String { get }
    static var nullable: Bool { get }
}

extension ColumnMappable {
    // A default
    static var nullable: Bool { false }
}

extension String: ColumnMappable {
    static let typeStr: String = "text"
}

extension RawRepresentable where RawValue: ColumnMappable {
    static var typeStr: String { RawValue.typeStr }
    // This is the `nullable` I want, but compiler doesn't know that.
    static var nullable: Bool { RawValue.nullable }
}

// Error here
enum MyEnum: String, ColumnMappable { case a, b, c }

The error points to the 2 definitions of nullable and says:

Multiple matching properties named 'nullable' with type 'Bool'

Make the last extension include a Self: ColumnMappable constraint.

1 Like

Man, you guys are good. :) I'm happy it works, but I don't see why it does. Do you?

In your original code, there are two independent implementations: one from ColumnMappable and one from RawRepresentable.

By adding the constraint, the second implementation is no longer independent: both fall under ColumnMappable, and the more-specific one is chosen.

• • •

From a purely human-readability standpoint, one might prefer to write:

extension ColumnMappable where Self: RawRepresentable, RawValue: ColumnMappable

So that all the implementations are visually-obviously in extensions of ColumnMappable.

2 Likes