# 構造体とは

構造体とは、値型の一種です。プロパティやメソッドを定義することによって様々なデータや振舞いを形成することができます。

# 構造体の定義方法

構造体のの定義方法は、struct キーワードを使って宣言します。

struct 構造体の名前 {}

次の例では、構造体の中で、ストアドプロパティやコンピューテッドプロパティ、メソッドなどを定義しています。

struct Person {
  // ストアドプロパティを定義
  var name = "Taro"

  // コンピューテッドプロパティを定義
  var age: Int {
    return 10
  }

  // メソッドを定義
  func printName() {
    print(self.name)
  }
}

// インスタンスを生成する
let person1 = Person()

person1.name        // Taro
person1.age         // 10
person1.printName() // Taro

# 構造体の特徴

Swiftの構造体は次のような特徴があります。

# 値型

構造体は値型であり、基本的な型であるInt型や、Float型、Bool型、String型、Array型、Dictionary型などは全て内部では構造体として実装されています。

値型の詳しい説明は、値型と参照型の章の 値型 を参照してください。

# 複数の値を一つの構造体で管理できる

構造体では、プロパティの組み合わせによって一つの値を表現することができます。変数や定数、クラス、列挙型などを一つの構造体に定義することによってデータの管理がしやすくなります。

struct Person {
  // ストアドプロパティを定義
  var name: String
  var age: Int

  // コンピューテッドプロパティを定義
  var nameAndAge: String {
    return "\(name) さんは、\(age) 歳です"
  }

  // 列挙型を定義
  enum BloodType {
    case A, B, O, AB
  }

  // メソッドを定義
  func printName {
    print(self.name)
  }
}

# 同じ構造のデータを複製できる

構造体は、インスタンスを生成することによって同じ構造のデータを複製することができます。また、値型なので異なる変数に代入してもコピーされ、それぞれが独立したデータとして扱うことができます。

struct Person {
  var name: String
  var age: Int
}

// インスタンスを生成して同じ構造のデータをつくる
var person1 = Person(name: "Taro", age: 20)
var person2 = Person(name: "Jiro", age: 18)
var person3 = Person(name: "Saburo", age: 15)

// 新しい変数に代入しても値の共有はされない
var person4 = person1
person4.name = "Siro"

person1.name // Taro
person4.name // Siro

値型と参照型の振舞いの違いについては、値型と参照型 をご確認ください。

# 定数への変更はできない

構造体のストアドプロパティへの変更は、プロパティの再代入をする必要があるため、定数で代入されたインスタンスへ変更をしようとするとコンパイラはエラーを出力します。

struct Person {
  var name: String
  var age: Int
}

// 定数で代入
let person1 = Person(name: "Taro", age: 20)

// プロパティを変更
person1.name = "Jiro"

/* 実行結果 */
// error: cannot assign to property: 'person1' is a 'let' constant
// person1.name = "Jiro"
// note: change 'let' to 'var' to make it mutable
// let person1 = Person(name: "Taro", age: 20)

# mutating

構造体のメソッドの中でストアドプロパティを変更するには、mutating キーワード を使用します。

struct Person {
  var name: String
  var age: Int

  // mutating キーワードを使ってメソッドを定義
  mutating func updateName(name: String) {
    self.name = name
  }
}

var person1 = Person(name: "Taro", age: 20)

// プロパティを変更
person1.updateName(name: "Jiro")
person1.name // Jiro

mutating キーワード がないと、コンパイラはエラーを出力します。

struct Person {
  var name: String
  var age: Int

  // mutating キーワードをなしでメソッドを定義する
  func updateName(name: String) {
    self.name = name
  }
}

var person1 = Person(name: "Taro", age: 20)

person1.updateName(name: "Jiro")

/* 実行結果 */
// error: cannot assign to property: 'self' is immutable
//     self.name = name
// note: mark method 'mutating' to make 'self' mutable
//   func updateName(name: String) {
//   mutating 

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

型のインスタンスは、インスタンス化が完了される前にすべてのプロパティに値が入っていなければなりません。値をセットするには宣言時にデフォルト値を代入するか、イニシャライザで行う必要があります。

構造体では、ストアドプロパティにデフォルト値がなく、イニシャライザを定義していなくても、メンバーワイズイニシャライザが暗黙的に作られます。

メンバーワイズイニシャライザとは、明示的に init でイニシャライザを定義しなくても自動的に作られ、型が持っているストアドプロパティと同名の引数を指定して初期化できる機能です。

通常、イニシャライザを定義して書くと次のようになります。

struct Person {
  var name: String
  var age: Int

  // イニシャライザを定義する
  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }
}

var person1 = Person(name: "Taro", age: 20)

しかし、イニシャライザを省略すると、メンバーワイズイニシャライザが自動的に作られて同様の引数でインスタンスを生成することができます。

struct Person {
  var name: String
  var age: Int

  // 省略しても同じ引数で初期化できる
  // init(name: String, age: Int) {
  //   self.name = name
  //   self.age = age
  // }
}

var person1 = Person(name: "Taro", age: 20)