I'm trying to define a protocol that requires an enum
with raw value String
to be implemented.
I don't believe that it's currently possible to enforce the use of enum
, and I'm not sure I really care as long as somewhere down the line I can call fromRaw()
and receive a String
.
So, I'm trying to maintain the brevity of the following while restricting Beta
to be an enum
where the raw value is a 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"
The problem with the above is that other raw values are allowed, and therefore this is valid:
struct Epsilon: Alpha {
enum Beta: Int {
case Zeta = 6
}
}
let epsilon = Epsilon()
Eta(alpha: epsilon, beta: .Zeta) // "beta is 6"
To address that I'm currently doing this:
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"
Is there a way that I can declare the typealias
differently in the original example to restrict RawRepresentable
to String
?
Specifying U: RawRepresentable where U.Raw == String
seemed hopeful, so I gave that a try:
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
While this technically prevents using anything other than a String
, I'm looking for a compile-time constraint and this appears to be causing a runtime exception.
I'd also prefer that this be enforced by the protocol if possible rather than consumers needing to check that .Raw == String
Just to add onto this since it's a bit older, your updated example works in swift 2+ now and will complain at compile time that .Zeta is ambiguous unless it's a String type.
You can also put the check in a pattern match for a protocol extension. As an example:
extension SequenceType where Generator.Element:RawRepresentable,
Generator.Element.RawValue == String {
func toStringArray() -> [String] {
return self.map { $0.rawValue }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With