enum と構造体の違い
Swiftにおける enum(列挙型)と struct(構造体)の主な違いは、表現したいデータの性質にあります。どちらもメソッドやプロパティを持つことができる値型ですが、用途が異なります。
主な違い
| 比較項目 | enum(列挙型) | struct(構造体) |
|---|---|---|
| 意味・用途 | 限定された「選択肢」や「状態」を表す。 | 複数の「データ(プロパティ)のまとまり」を表す。 |
| 表現方法 | 複数のcase(ケース)のいずれか1つである状態を表現する。 | 複数の異なるデータを同時に保持する構造を表現する。 |
| インスタンス化 | 特定のケースとしてのみ存在する。 | データを持った具体的なインスタンスとして生成される。 |
| 関連値 | 各ケースに異なる型の関連値(Associated Values)を持たせることができる。 | 独自のプロパティ(stored properties)を持つ。 |
| データの持ち方 | 定義されたケースのどれか一つになる | 複数の値を同時に保持する |
| 主な役割 | 「種類や状態」を表現する | 「もの(Entity)」を表現する |
| 得意なこと | 条件分岐(switch 文との相性が抜群) | 複雑な属性を持つデータの管理 |
| 拡張性 | associated value でケースごとに異なるデータを持たせる | プロパティを増やして情報をリッチにする |
詳細な説明
enum(列挙型)
enumは、取りうる値が固定されていて、そのうちの1つだけを選択的に表現したい場合に使用します。
- 例: 信号機の色 (
赤,黄,青)、ユーザーの状態 (ログイン中,ログアウト中)、曜日など。 - 各ケースに異なるデータを付加できる「関連値」の機能が強力で、例えばエラーの種類とそれに付随する詳細情報を一緒に表現するのに適しています。
|
1 2 3 4 5 6 7 8 9 10 |
enum TrafficLight { case red case yellow case green } enum APIError { case networkError(code: Int, message: String) case notFound } |
struct(構造体)
structは、複数の異なるプロパティを組み合わせて一つのまとまったデータ構造を表現したい場合に使用します。
- 例: ユーザー情報 (
名前,年齢,住所)、座標 (x,y)、商品の詳細情報など。 - データそのものをモデル化し、そのデータの塊に対して振る舞い(メソッド)を追加するのに適しています。
|
1 2 3 4 5 6 7 8 9 10 |
struct User { var name: String var age: Int var address: String } struct Point { var x: Double var y: Double } |
使い分けの判断基準
迷ったときは、以下の質問を自分に投げかけてみてください。
「それは『属性のリスト』ですか?」
- Yes →
struct
「それは『限られた選択肢』ですか?」
- Yes →
enum
まとめ
どちらも値型であり、メソッドを定義できるなど共通点も多いですが、**「閉じた選択肢の集合」を扱いたい場合はenum、「複数のプロパティで構成されるデータの塊」**を扱いたい場合はstructを選ぶのが基本的な使い分けとなります。
none
enum(列挙型)で「値がない」状態や「なし」を表すケースを定義する方法は、主に以下の2通りです。
1. 単純なケースとして定義する
最も一般的な方法は、none という名前のケースを明示的に作成することです。
|
1 2 3 4 5 6 7 |
enum Status { case none case pending case success } let currentStatus = Status.none |
2. Optional型(nil)を利用する
Swift標準のOptional型は、内部的に case none を持つ enum です。独自の enum に none を作らず、「値そのものがない可能性」を Optional で表現するのがSwiftらしい設計です。
|
1 2 3 4 5 6 7 |
enum Status { case pending case success } // nil が「なし」を意味する var currentStatus: Status? = nil |
注意点:Optional.none との名前衝突
自分自身で case none を定義すると、Optional型の .none(つまり nil)と名前が衝突し、コンパイラが混乱することがあります。
|
1 2 3 4 5 6 7 |
enum MyEnum { case none case someValue } let value: MyEnum? = .none // これは MyEnum.none なのか、Optional<MyEnum>.none (nil) なのか曖昧になる |
解決策・推奨される命名:
none以外の名前を使う:case empty,case undefined,case unknownなど。- Optionalを使う: そもそも
case noneを作らずMyEnum?型として扱う。 - 明示的に指定する:
Optional.noneまたはMyEnum.noneと記述する。
フィルタリング
SwiftUIのForEachで特定のケース(emptyなど)を除外してViewを生成するには、主に「フィルタリングした配列を渡す」か「ループ内で条件分岐する」かの2通りの書き方があります。
1. 配列をフィルタリングして渡す(推奨)
ForEachに渡す前に .filter を使って特定のケースを除外します。Viewのコードがスッキリし、不要な計算も抑えられるため、こちらの方法が推奨されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
enum Status: String, CaseIterable, Identifiable { case empty case pending case success var id: String { self.rawValue } } struct MyView: View { var body: some View { VStack { // empty 以外のケースだけを抽出してループ ForEach(Status.allCases.filter { $0 != .empty }) { status in Button(status.rawValue) { print("\(status) tapped") } } } } } |
2. ループ内で if 分岐する
ForEach のクロージャ内で条件判定を行い、表示したくないケースのときに何も返さない(あるいは EmptyView() を返す)方法です。
|
1 2 3 4 5 6 7 |
ForEach(Status.allCases) { status in if status != .empty { Button(status.rawValue) { // アクション } } } |
どちらを使うべきか?
- 基本は「1. フィルタリング」:
ForEachに渡すデータそのものを絞り込む方が、SwiftUIの描画効率やコードの可読性の面で優れています。 - 計算プロパティに切り出す:フィルタリング処理をViewの中に直接書くのが長い場合は、以下のように
enumやViewの計算プロパティとして定義しておくとさらに綺麗になります。
|
1 2 3 4 5 6 7 8 9 |
extension Status { /// ボタンとして表示したいケースのみを返す static var buttonCases: [Status] { allCases.filter { $0 != .empty } } } // 呼び出し側 ForEach(Status.buttonCases) { status in ... } |
補足
CaseIterable プロトコルを enum に採用することで、 allCases という全ケースの配列が自動生成されるようになります。これを利用することで簡単にフィルタリングが可能です。
VBA の enum との違い
VBAのEnumとSwiftのenumは、名前は同じですが性質と機能が大きく異なります。
VBAのEnumが単なる整数の定数に別名を付けたエイリアス(別名)であるのに対し、Swiftのenumは遥かに強力で、言語の重要な構成要素の一つです。
VBAのEnumの性質
VBAのEnumは、基本的に整数値のグループ化と可読性の向上を目的としています。
|
1 2 3 4 5 6 7 |
' VBAの例 Public Enum Color Red = 1 Green = 2 Blue = 3 End Enum ' 内部的には単なる整数 (Long型) |
Swiftのenumの強力な点
Swiftのenumは、VBAとは異なり、以下のような高度な機能を持っています。
- 関連値 (Associated Values):
各ケースが異なる型のデータを持つことができます。これがVBAにはない最大の機能です。(前の回答のAPIError.networkError(code: Int, message: String)のように) - メソッドとプロパティ:
structやclassと同様に、計算プロパティやインスタンスメソッドを持つことができます。 - 値型としての振る舞い:
structと同様に値型として扱われます。
まとめ
VBAからSwiftへ移行される場合、Swiftのenumは「単なる定数」ではなく、「高度な機能を持つ独自のデータ型」として捉える必要があります。この違いを理解すると、Swiftでのデータモデリングの幅が大きく広がります。
コメント