SwiftでC言語のunion的な使い方
先日勉強会でSwiftにはC言語の共用体unionってないのでしょうかという質問があったので、Swiftでunion的な使い方を紹介したいと思います。
enumをC言語の共用体として使う
Swiftの予約後にはunion keywordはありません。Swift のドキュメント*1 にはバーコードの値を扱うenum Barcodeの例が出ています。UPCAとQRCodeでは扱うデータの型が異なるため同じ型として扱いにくいのは確かですが、Swiftのenumを使えば、unionのように扱う事ができます。
enum Barcode {
	case UPCA(Int, Int, Int, Int)
	case QRCode(String)
}
UPCAとQRCodeでは異なるデータの型を扱いたいので、それぞれのキーワードの後に括弧で扱いたい型を指定すれば良いです。これを 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節の中のletやvarで値を取り出します。
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.String や Swift.Float は ネームスペースの問題で Swiftがついていますが、今回は割愛させていただきます。
*1 The Swift Programming Language (Swift 2 Prerelease)