LoginSignup
11
16

More than 5 years have passed since last update.

SwiftでC言語の共用体(union)的な使い方

Last updated at Posted at 2015-12-08

SwiftでC言語のunion的な使い方

先日勉強会でSwiftにはC言語の共用体unionってないのでしょうかという質問があったので、Swiftでunion的な使い方を紹介したいと思います。

enumをC言語の共用体として使う

Swiftの予約後にはunion keywordはありません。Swift のドキュメント*1 にはバーコードの値を扱うenum Barcodeの例が出ています。UPCAQRCodeでは扱うデータの型が異なるため同じ型として扱いにくいのは確かですが、Swiftのenumを使えば、unionのように扱う事ができます。

enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)

}

UPCAQRCodeでは異なるデータの型を扱いたいので、それぞれのキーワードの後に括弧で扱いたい型を指定すれば良いです。これを Associated Value と言います。

let barcode1 = Barcode.UPCA(8, 85909, 51226, 3)
let barcode2 = Barcode.QRCode("Hello World")

こうなれば、UPCAもQRCodeも一つの型として扱いやすいので、Anyじゃなくても配列として扱える。

let barcodes: [Barcode] = [barcode1, barcode2]

Associated Value を取り出す

では、Barcodeの値を引き渡された関数を想定して、その値をprintしてみましょう。Optional型がUnwarpしないと値が取り出せないように、enumのassociated valueも値を取り出すような一手間が必要になります。case節の中のletvarで値を取り出します。

func process(barcode: Barcode) {
    switch barcode {
    case .UPCA(let a, let b, let c, let d):
        print("UPCA: \(a),\(b),\(c),\(d)")
    case .QRCode(let string):
        print("QR: \(string)")
    }
}

もちろん値を取り出す必要がない場合は括弧全体を省略できます。

    case .UPCA: print("UPCA")
    case .QRCode: print("QRCode")

一つの型のみ注力して値を取り出したい場合は、まぁswitch文を使ってもいいのですが、if 文でも書く事が出来ます。if letといった書き方とずいぶん雰囲気が変わるので、面食らうかもしれませんが、慣れると簡潔にかけていいかもしれません。

if case .QRCode(let string) = barcode {
    print("\(string)")
}

ちょっと一工夫でより便利に

こうした associated valueの取り出すコードを何度も繰り返さなければいけない場合が予想される場合は、enumの型自体に、メソッドなどを用意すると便利になる場合があります。

enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)

    var UPCA: (Int, Int, Int, Int)? {
        if case .UPCA(let a, let b, let c, let d) = self {
            return (a, b, c, d)
        }
        return nil
    }

    var QRCode: String? {
        if case .QRCode(let string) = self {
            return string
        }
        return nil
    }
}
if let code = barcode.QRCode {
    print("\code")
}

JSONの要素などの表現

このようにenumを使えば、JSONの要素のように、値の型がはっきりしない要素を扱う事が容易になります。この場合Nullには associated value は必要ありませんね。

enum Element {
    case String(Swift.String)
    case Boolean(Bool)
    case Integer(Int)
    case Float(Swift.Float)
    case Dictionary([Swift.String: Element])
    case Array([Element])
    case Null
}

Swift.StringSwift.Float は ネームスペースの問題で Swiftがついていますが、今回は割愛させていただきます。


*1 The Swift Programming Language (Swift 2 Prerelease)

11
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
16