Swift: 协议 `static var foo: Self` 和枚举

Swift: Protocol `static var foo: Self` and enums

简化示例

看看这个简单的协议

protocol FooOwner {
    static var foo: Self { get }
}

我希望枚举符合上述协议,在我看来这应该可行,因为静态语法 SomeTypeConformingToFooOwner.foo 应该导致 SomeTypeConformingToFooOwner 的实例,并且在以下情况下SomeTypeConformingToFooOwner 是一个枚举。

enum Foo: FooOwner { // Type 'Foo' does not conform to protocol FooOwner
    case foo
}

解决方法是这个丑陋的东西:

protocol FooOwner {
    static var fooOwner: Self { get }
}

enum Foo: FooOwner {
    case foo
    static var fooOwner: Foo {
        return Foo.foo
    }
}

对于符合静态变量协议的枚举,您有更好的解决方法吗?

真实用例

protocol StringConvertibleError: Swift.Error {
    static var invalidCharactersError: Self { get }
}

protocol StringConvertibleErrorOwner {
    associatedtype Error: StringConvertibleError
}

protocol StringConvertible {
    var value: String { get }

    /// Calling this with an invalid String will result in runtime crash.
    init(validated: String)

    init(string value: String) throws

    static func validate(_ string: String) throws -> String
}

// MARK: - Default Implementation Constrained
extension StringConvertible where Self: CharacterSetSpecifying, Self: StringConvertibleErrorOwner {
    static func validate(_ string: String) throws -> String {
        guard Self.allowedCharacters.isSuperset(of: CharacterSet(charactersIn: string)) else {
            throw Error.invalidCharactersError
        }
        // Valid
        return string
    }
}

struct HexString: StringConvertible, CharacterSetSpecifying, StringConvertibleErrorOwner {

    static var allowedCharacters = CharacterSet.hexadecimal

    let value: String

    init(validated unvalidated: String) {
        do {
            self.value = try HexString.validate(unvalidated)
        } catch {
            fatalError("Passed unvalid string, error: \(error)")
        }
    }
}

extension HexString {
    enum Error: StringConvertibleError {
        static var invalidCharactersError: Error {
            return Error.invalidCharacters
        }

        case invalidCharacters
    }
}

所以这是我想更改的最后一部分:

extension HexString {
    enum Error: StringConvertibleError {
        case invalidCharacters
    }
}

因为我有很多和HexString相似的类型。

当然可以,显然我可以使用一个共享的错误枚举,但我希望每种类型都有一个特定的枚举。

SE-0280 解决这个问题 - 如果被接受。目前正在审核中。

编辑 1

好消息,SE-0280 has been accepted

这可能与 OP 的问题不完全匹配,但由于它看起来有些相关,所以我将其扔掉以防万一。 就我而言,我关心某些情况,但可以忽略其他情况(或以通用方式处理)。 我承认我的方法并不优雅,需要大量样板,但它让我解决了类似的问题。

// Given these enums, we want a protocol that matches enums with
// 'foo' and 'bar' cases.
enum FooFriends {
    case foo // conforms
    case bar // conforms
    case baz // don't really care
}

enum FooBar {
    case foo // conforms
    case bar // conforms
}

// We wish we could do this:
protocol FooBarWish {
    case foo // or static var foo: Self { get }
    case bar // or static var bar: Self { get }
}

// Workaround
// the boiler plate
enum FooBarProxy {
    case foo
    case bar
    case other
}

protocol FooBarProtocol {
    func getProxy() -> FooBarProxy
}

extension FooFriends: FooBarProtocol {
    func getProxy() -> FooBarProxy {
        switch self {
        case .foo:
            return .foo
        case .bar:
            return .bar
        default:
            return .other
        }
    }
}

extension FooBar: FooBarProtocol {
    func getProxy() -> FooBarProxy {
        switch self {
        case .foo:
            return .foo
        case .bar:
            return .bar
        }
    }
}

// Usage
// Instead of the ideal case (which won't work)
let fooBarOrFooFriend1: FooBarWish = FooFriends.foo
let fooBarOrFooFriend2: FooBarWish = FooBar.bar

// We can get by with
let fooBarProxy1 = FooFriends.foo.getProxy()
let fooBarProxy2 = FooBar.bar.getProxy()

// Verification
func checkIt(_ foobar: FooBarProxy) {
    switch foobar {
    case .foo:
        print("it was foo")
    case .bar:
        print("it was bar")
    case .other:
        print("it was neither")
    }
}

checkIt(fooBarProxy1)
checkIt(fooBarProxy2)
// =>
// it was foo
// it was bar