Swift1.2でenumにジェネリクス使ってその型を束縛できないって言ってるやつの意味が分かった

少し前のことだけど、今のプロジェクトが始まった頃にコード書いてて理解できたのでメモ。

ジェネリクスがない場合

成功か失敗か返すのにEnumを作るとしてジェネリクスがないとこうなっちゃう。

enum Result {
    case Success(AnyObject)
    case Failure(NSError)
}

使う方としては、

let result: Result = .Success("hoge")

switch result {
case .Success(let obj):
    if let str = obj as? String {
        // objがAnyObjectなのでキャストできるか調べないとダメ
        // しかも今回はStringが返ってくるってことを覚えておかないといけない
        println(str) 
    }
case .Failure(let error):
    // こっちはNSErrorって分かってる
    println(error.description)
}

ジェネリクスの場合

こうしたい。

enum Result<T> {
    case Success(T)
    case Failure(NSError)
}

使う方としてはこうあってほしい。

let result: Result = .Success("hoge")

switch result {
case .Success(let str):
        // strはString型って分かってるからキャストいらない
        println(str) 
    }
case .Failure(let error):
    // こっちはNSErrorって分かってる
    println(error.description)
}

でも、コンパイルエラーでダメ

Unimplemented IR generation feature non-fixed multi-payload enum layout

ジェネリクスに渡す値をクラスにする

堅牢で使いやすいAPIクライアントをSwiftで実装したいを参考にBoxを用意してあげる。こう書く。なるほど。

public class Box<T> {
    public let value: T
    
    public init(_ value: T) {
        self.value = value
    }
}

public enum Result<T> {
    case Success(Box<T>)
    case Failure(Box<NSError>)
    
    public init(_ value: T) {
        self = .Success(Box(value))
    }
    
    public init(_ error: NSError) {
        self = .Failure(Box(error))
    }
}

使うほう

let result: Result = .Success(Box("hoge"))

switch result {
case .Success(let box):
        // boxに包まれてる値であるvalueはStringて分かってる
        println(box.value) 
    }
case .Failure(let box):
    // こっちはNSErrorって分かってる
    println(box.value.description)
}