Swiftで特定の複数の型のみを許容するArray

1つの型を許す配列はもちろん初心者でもすぐ出来ると思います。
では例えば、 `Int` と `Double` のみを許す数値の配列を用意したい場合、どうすればいいでしょう。
手段と長短をまとめました。

# 手段
## Enum を利用

```swift
enum EnumNumber {
    case int(Int)
    case double(Double)
}

var enumNumbers: [EnumNumber] = [.int(10), .double(2.0)]
```

## Protocol を利用

```swift
protocol ProtocolNumber {}
extension Int: ProtocolNumber {}
extension Double: ProtocolNumber {}

var protocolNumbers: [ProtocolNumber] = [Int(10), Double(2.0)]
```

# 長所と短所

## switch で値を取り出す

`Protocol` を使用した場合だと、全パターンを書いたとしても `default` 行を書かなくてはならないので、少し安全ではないコードになってしまうかも。

```swift
// Enum
switch enumNumbers.first! {
case .int(let value):       print(value)
case .double(let value):    print(value)
}

// Protocol
switch protocolNumbers.first! {
case let value as Int:      print(value)
case let value as Double:   print(value)
default:                    print("exception")
}
```



## 型が分かっている物の値を取得

`Protocol` だとキャストするだけで値が取れる( optional もしくは forced unwrap ですが )のに対し、
`Enum` だとおそらく `if case` 文を書かないといけないのではと思います。となるとインデントが深くなってしまいます。
インデントが深くなるのを回避するために **パターン2** のように computed property を生やす手がありますが、もしこうするならば型の数だけ用意しなければなりません。

```swift
// Enum パターン1
if case .int(let value) = enumNumbers[0] {
    let enumInt = value
}

// Enum パターン2
extension EnumNumber {
    var integerValue: Int? {
        if case .int(let value) = enumNumbers[0] {
            return value
        }
        return nil
    }
}
let enumInt = enumNumbers[0].integerValue

// Protocol
let protocolInt = enumNumbers[0] as? Int
```

## 生成する時

大きな違いは、 `Protocol` の場合は **Int か Double か分かる必要が無い** という点です。
`Enum` の場合は **Int か Double か把握した上で Enum を生成** する必要があります。
明らかに `Enum` のほうが面倒くさくなると思います。

```swift
let unknownValue: Any = Int(123)

// Enum パターン1
// このパターンだと、unknownValue が Double の場合、追加されない
if let intValue = unknownValue as? Int {
    enumNumbers.append(.int(intValue))
}

// Enum パターン2
extension EnumNumber {
    init?(_ value: Any) {
        switch value {
        case let value as Int:
            self = .int(value)
        case let value as Double:
            self = .double(value)
        default:
            return nil
        }
    }
}
if let value = EnumNumber(unknownValue) {
    enumNumbers.append(value)
}

// Protocol
if let value = unknownValue as? ProtocolNumber {
    protocolNumbers.append(value)
}
```

## ネストしたい

Swift の設計上 `Protocol` はネストできません。

```swift
class Sample {
    
    // OK
    enum EnumNumber {}
    
    // error: protocol 'ProtocolNumber' cannot be nested inside another declaration
    protocol ProtocolNumber {}
}
```

# まとめ

主観で ○ × つけました。

||Enum|Protocol|
|---|:--:|:--:|
|switch で値を取り出す|○|×|
|型が分かっている物の値を取得|×|○|
|生成する時|×|○|
|ネストしたい|○|×|

~~まあこんな色々書きましたが、基本的には `Protocol` を使うべきですね笑~~

# 追記: 折衷案

@herara_ofnir3 さんに[コメント](https://qiita.com/yuki0n0/items/4d8de657d49a8d2d27da#comment-092e51527e5e45e66d92)をいただきました！
上記の表で見ても良いとこ取りできてます。

```swift
enum EnumNumber {
    case int(Int)
    case double(Double)
}

protocol EnumNumberConvertible {
    var enumNumber: EnumNumber { get }
}

extension Int : EnumNumberConvertible {
    var enumNumber: EnumNumber { return .int(self) }
}

extension Double : EnumNumberConvertible {
    var enumNumber: EnumNumber { return .double(self) }
}

// 配列を用意
var numbers: [EnumNumberConvertible] = [10, 0.2]

// 1. switch で値取り出し
for number in numbers {
    switch number.enumNumber {
    case .int(let value):    print(value)
    case .double(let value): print(value)
    }
}

// 2. 型が分かっている物の値を取得
let value = numbers[0] as? Int

// 3. 生成する時
let unknownValue: Any = Int(123)
if let value = unknownValue as? EnumNumberConvertible {
    numbers.append(value)
}
```

# 参考
- https://stackoverflow.com/questions/49008269

# 環境
- Swift 5.0

他にもなにかありましたら教えてください！