【Swift】型の種類〜構造体〜

#構造体 とは

構造体は値型の一種であり、
ストアドプロパティの組み合わせによって１つの値を表します。

構造体の利用例は多岐に渡ります。
例えば標準ライブラリで提供されている多くの型は構造体です。

Bool型、数値型、String型、Array\<Element>型は全て構造体です。

構造体ではないの型は、
Optional\<Wrapped>型、タプル型、Any型などが挙げられます。

##定義方法

構造体の定義には`structキーワード`を用います。

```swift

struct 構造体名 {
   構造体の定義
}
```

プロパティやメソッド、イニシャライザなどの
型を構成する要素では全て利用可能で、{ }内に定義します。

```swift

struct Sample {
    let number: Int
    let name: String
    
    init(number: Int, name: String) {
        self.number = number
        self.name = name
    }
    
    func printStatus() {
        print("名前：\(name)\n番号：\(number)番")
    }
}

let a = Sample(number: 1, name: "相原")
a.printStatus()

実行結果
名前：相原
番号：1番
```


##ストアドプロパティの変更による値の変更

先ほど記載した通り、構造体はストアドプロパティの組み合わせになります。
構造体のストアドプロパティを変更することは構造体を別の値に変更することに等しく、
構造体が入っている定数や変数の再代入を必要とします。

したがって、**値型の値の変更に関する仕様は、
構造体のストアドプロパティの変更にも適用されます。**

###定数のストアドプロパティは変更不可

構造体のストアドプロパティの変更には再代入を必要とするので、
定数に代入された構造体のストアドプロパティは変更することができません。

下記サンプルコードの通りです。

```swift

struct Sample {
    var value: Int
    
    init(value: Int) {
        self.value = value
    }
}

var a = Sample(value: 1)
a.value = 2   // OK

let b = Sample(value: 1)
b.value = 3   // コンパイルエラー
```

エラー内容：`Cannot assign to property: 'b' is a 'let' constant`
和訳：プロパティに割り当てることができません：「b」は「let」定数です

###メソッド内のストアドプロパティの変更

構造体の中に定義されているメソッドでストアドプロパティを変更するためには、
メソッドの先頭にmutatingキーワードを追加する必要があります。

```swift

struct Sample {
    var value: Int
    
    init(value: Int) {
        self.value = value
    }
    
    mutating func increment() {
        value += 1
    }
}

var a = Sample(value: 1)
a.increment()
a.value   // 2
```

メソッド内でのストアドプロパティの変更はコンパイラによってチェックされています。

なので、ストアドプロパティの変更を伴っているメソッドに
mutatingキーワードが追加されていないとコンパイルエラーになります。

下記がmutatingキーワードが追加されていないパターンです。

```swift

struct Sample {
    var value: Int
    
    init(value: Int) {
        self.value = value
    }
    
    func increment() {
        value += 1   // コンパイルエラー
    }
}

var a = Sample(value: 1)
a.increment()
```

エラー内容：`Left side of mutating operator isn't mutable: 'self' is immutable`
和訳：変更演算子の左側は変更できません：「self」は変更できません

##メンバーワイズイニシャライザ

型のインスタンスは初期化後に全てのプロパティが初期化されている必要があります。

そのため、プロパティの定義時に初期化したり、
独自のイニシャライザを定義して初期化していました。

ですが、**構造体では自動的に定義される
メンバーワイズイニシャライザを利用することができます。**

メンバーワイズイニシャライザとは、
型が持っている各ストアドプロパティと同名の引数を取るイニシャライザです。

下記サンプルコードの場合は、
ストアドプロパティがvalueとstatusの２つなので
メンバーワイズイニシャライザは`init(value: Int, status: String)`になります。

また、メンバーワイズイニシャライザは、
通常のイニシャライザと同様に使うことができます。

```swift

struct Sample {
    var value: Int
    var status: String
    
    // 自動で生成される
    // init(value: Int, status: String) {
    //     self.value = value
    //     self.status = status
    // }

}

let a = Sample(value: 123, status: "abc")
a.value   // 123
a.status   // abc
```

###メンバーワイズイニシャライザのデフォルト引数

ストアドプロパティが定義時に初期化されている場合、
そのプロパティに対応するメンバーワイズイニシャライザの引数は、
**デフォルト引数を持ち呼び出し時の引数の指定を省略できます。**

もちろんデフォルト引数を持つだけなので、
通常通り引数を指定することもできます。

次のサンプルコードでは、
ストアドプロパティvalueとstatusを持っている構造体を定義します。

valueは初期化を行い10を代入しています。

この場合メンバーワイズイニシャライザの引数valueには、
デフォルト引数10が定義されます。

```swift

struct Sample {
    var value: Int = 10
    var status: String
    
    // 自動で生成される
    // init(value: Int = 10, status: String) {
    //     self.value = value
    //     self.status = status
    // }
}

let a = Sample(status: "abc")
a.value   // 10
a.status   // abc

let b = Sample(value: 123, status: "def")
b.value   // 123
b.status   // def
```

以上が構造体についての説明になります。

型の構成要素をうまく使い利便性の高い構造体を作れるようになりたいです。

そのためにはどんどん構造体を利用していくしかないと思うので、
どんどんアウトプットしていきたいと思います！

クラスや列挙型、型の基礎知識についても記事にしていますので、
お時間がある際にぜひご覧ください！

・[【Swift】型の種類〜基礎知識〜](https://qiita.com/onishiApp/items/173a1bcf7d75e3ba095f)
・[【Swift】型の種類〜クラス前編〜](https://qiita.com/onishiApp/items/a34d7408a4b123d9d11f)
・[【Swift】型の種類〜列挙型〜](https://qiita.com/onishiApp/items/1650c4e581fc9c8d3255)

最後までご覧いただきありがとうございました。