В Swift, как я могу указать typealias, который ограничивает RawRepresentable для String?

Я пытаюсь определить протокол, который требует enum с необработанным значением String быть реализованным.

Я не верю, что в настоящее время возможно навязать использование enumи я не уверен, что мне все равно, пока я могу позвонить fromRaw() и получить String,

Итак, я пытаюсь сохранить краткость следующего, ограничивая Beta быть enum где необработанное значение является String:

protocol Alpha {
    typealias Beta: RawRepresentable
}

struct Gamma: Alpha {
    enum Beta: String {
        case Delta = "delta"
    }
}

struct Eta<T: Alpha, U: RawRepresentable where T.Beta == U> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
Eta(alpha: gamma, beta: .Delta) // "beta is delta"

Проблема с вышеупомянутым состоит в том, что другие необработанные значения допустимы, и поэтому это допустимо:

struct Epsilon: Alpha {
    enum Beta: Int {
        case Zeta = 6
    }
}

let epsilon = Epsilon()
Eta(alpha: epsilon, beta: .Zeta) // "beta is 6"

Для решения этой проблемы я сейчас занимаюсь этим:

protocol StringRawRepresentable: RawRepresentable {
    class func fromRaw(raw: String) -> Self?
}

protocol Alpha {
    typealias Beta: StringRawRepresentable
}

struct Gamma: Alpha {
    enum Beta: String, StringRawRepresentable {
        case Delta = "delta"
    }
}

// Type 'Epsilon' does not conform to protocol 'Alpha'
struct Epsilon: Alpha {
    enum Beta: Int {
        case Zeta = 6
    }
}

struct Eta<T: Alpha, U: StringRawRepresentable where T.Beta == U> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
Eta(alpha: gamma, beta: .Delta) // "beta is delta"

Есть ли способ, которым я могу объявить typealias иначе в исходном примере ограничить RawRepresentable в String?


Обновить

Определение U: RawRepresentable where U.Raw == String казалось обнадеживающим, поэтому я попробовал:

protocol Alpha {
    typealias Beta: RawRepresentable
}

struct Gamma: Alpha {
    enum Beta: String {
        case Delta = "delta"
    }
}

struct Eta<T: Alpha, U: RawRepresentable where T.Beta == U, U.Raw == String> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta

        // Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT).
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
Eta(alpha: gamma, beta: .Delta) // "beta is delta"

struct Epsilon: Alpha {
    enum Beta: Int {
        case Zeta = 6
    }
}

let epsilon = Epsilon()
Eta(alpha: epsilon, beta: .Zeta) // Error only occurs when this is executed

Хотя это технически не позволяет использовать что-либо, кроме StringЯ ищу ограничение времени компиляции, и это, кажется, вызывает исключение во время выполнения.

Я также предпочел бы, чтобы это обеспечивалось протоколом, если это возможно, а не потребителям, которые должны это проверять. .Raw == String

4 ответа

Просто добавьте к этому, так как он немного старше, ваш обновленный пример теперь работает в быстром 2+ и будет жаловаться во время компиляции, что.Zeta неоднозначен, если это не тип String.

Вы также можете поставить проверку в соответствии с шаблоном для расширения протокола. В качестве примера:

extension SequenceType where Generator.Element:RawRepresentable,
                         Generator.Element.RawValue == String {
    func toStringArray() -> [String] {
        return self.map { $0.rawValue }
    }
}

Теперь это должно быть возможно. Например, следующий протокол позволяет классу определять свое собственное перечисление "входных" параметров, соответствующих строке:

protocol AttributeContainer {
   associatedtype InputKey: RawRepresentable where InputKey.RawValue: StringProtocol
   func set(value: Any?, for inputKey: InputKey)
}

Это может быть использовано следующим образом:

class MyClass: AttributeContainer {

    enum AttributeKey: String {
       case attributeA, attributeB, attributeC
    }

    func set(value: Any?, for inputKey: InputKey) {
        // Handle the setting of attributes here
    }

}

Это похоже на то, как Apple обрабатывает CodingKey в Codable протокол. Я считаю это полезным при выполнении таких вещей, как хранение произвольных типов классов в базе данных.

Давайте посмотрим на наши варианты здесь. Во-первых, это (начиная с Xcode 6 beta 5) хорошо известное ограничение, что мы не можем указать ограничения типа enum простым и ожидаемым способом. Во-вторых, нужно что-то очень четкое: чтобы можно было звонить fromRaw(String), И в-третьих, вы хотите ошибку компилятора. Я бы сказал, что вам лучше всего написать протокол, чтобы сделать именно это, и довести до потребителя требование, чтобы он / она дал вам fromRaw(String), В этом случае вот что я бы сделал, упростив ваш второй фрагмент кода:

protocol Alpha {
    typealias Beta: RawRepresentable
    func fromRaw(raw: String) -> Beta?
}

struct Gamma: Alpha {
    enum Beta: String {
        case Delta = "delta"
        case Omega = "omega"
    }
    func fromRaw(raw: String) -> Beta? {
        return Beta.fromRaw(raw)
    }
}

struct Eta<T: Alpha, U: RawRepresentable where T.Beta == U> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
let a = Eta(alpha: gamma, beta: .Delta) // "beta is delta"
println(gamma.fromRaw("delta"))  // Optional(Enum Value)
println(gamma.fromRaw("omega")!) // Enum Value

С философской точки зрения это больше соответствует вашим потребностям: вы говорите: "Я хочу что-то не только RawRepresentable, но и fromRaw(String), Выясни, как ты это мне дашь ". Гамма-структура - самый простой пример, где потребитель указывает свое перечисление, а затем говорит:" Хорошо, я могу дать вам свой стандарт fromRaw() потому что это работает.

Мы с коллегой говорили об этом, и в Swift 2.0/2.1 вы можете сделать это с помощью протокола: https://gist.github.com/designatednerd/5645d286df0ce939714b

Пробовал в приложении, с которым я работаю, работает как шарм.:)

Другие вопросы по тегам