【Swift】プロトコルのプロパティ宣言時に記述する{ get }と{ get set }の意味とその違い

# 📚 参考

- [プロトコル(Protocols) / The Swift Programming Language(日本語版)](https://www.swiftlangjp.com/language-guide/protocols.html)
→ Swiftの公式ドキュメントの日本語版
- [Swift Protocols: Properties distinction(get, get set)🏃🏻‍♀️🏃🏻](https://chetan-aggarwal.medium.com/swift-protocols-properties-distinction-get-get-set-32a34a7f16e9)
→ SwiftにおけるProtocolの`{ get }` / `{ get set }`について言及した記事

# 🔰 `{ get }`と`{ get set }`の意味とその違い

:::note warn
プロパティ要件(Property Requirements)に記述する`{ get }`・`{ get set }`は、**プロトコル型インスタンスに対するパーミッション**であり、Computed Propertyの宣言時に記述する`get {...}`や`set {...}`とは**無関係**である
```swift:プロトコル型とは
protocol Animal {}
struct Dog: Animal {}

// ✅ プロトコル型インスタンス
let animal: Animal = Dog()
// ❎ プロトコル型インスタンスでない
let dog: Dog = Dog()
```
:::

```swift: プロトコルの宣言
protocol Sample {
  /**
    { get } .. プロトコル型インスタンスの場合は代入不可
    { get set } .. プロトコル型インスタンスの場合でも代入可能
   */
  var get_constant: Int { get }
  var get_variable: Int { get }
  var get_computed: Int { get }
  var get_private_set: Int { get }
  var getset_variable: Int { get set }
  var getset_computed: Int { get set }
}
```

```swift:プロトコルへの準拠(プロパティ要件)
struct SampleStructure: Sample {
  let get_constant: Int
  var get_variable: Int    // ✅ storedでもcomputedでもよい
  var get_computed: Int {  // ✅ storedでもcomputedでもよい
    get {
      return secret
    }
    set {  // ✅ プロパティ要件の{ get }とComputed Propertyの{ get set }は無関係
      print("private variable 'secret' value has changed.")
      secret = newValue
    }
  }
  private(set) var get_private_set: Int
  var getset_variable: Int
  var getset_computed: Int {
    get {
      return secret
    }
    set {
      print("private variable 'secret' value has changed.")
      self.secret = newValue
    }
  }

  // 説明用のPrivateプロパティ
  private var secret: Int = -1
}
```

:::note warn
Swiftは、基本的にプロトコル型は自身のプロトコルに準拠していない(= `self-conformance`をもたない)ものとする。

そうでないと、後述する`Parametric Polymorphism`においてはプロトコルのメンバが未定義動作を引き起こしてしまう。

```swift:Parametric Polymorphismによる未定義動作
func protocolTypeDoesNotConformProtocol<S: Sample>(_ sample: S) {
  let num: Int = sample.get_constant  //  ⚠ sampleがSampleインスタンスの場合は未初期化のため、未定義動作になる
}
```
:::

```swift:プロパティへの値の代入
struct SampleStructure: Sample {
  init() {
    get_constant = 0
    get_variable = 0
    get_computed = 0
    get_private_set = 0
    getset_variable = 0
    getset_computed = 0
  }

  mutating func mutate() {
    // get_constant = 1  ⛔ cannot assign to property: 'get_constant' is a 'let' constant
    get_variable = 1
    get_computed = 1
    get_private_set = 1
    getset_variable = 1
    getset_computed = 1
  }
}

// ✅ プロトコルに準拠したインスタンスからは{get}要件のプロパティに対して代入可能
var conformed: SampleStructure = SampleStructure()
// conformed.get_constant = 1  ⛔ cannot assign to property: 'get_constant' is a 'let' constant
conformed.get_variable = 1
conformed.get_computed = 1
// conformed.get_private_set = 1  ⛔ cannot assign to property: 'get_private_set' setter is inaccessible
conformed.getset_variable = 1
conformed.getset_computed = 1

// ❎ プロトコル型インスタンスからは{get}要件のプロパティに対して代入不可
var unconformed: Sample = SampleStructure()
// unconformed.get_constant = 1  ⛔ cannot assign to property: 'get_constant' is a get-only property
// unconformed.get_variable = 1  ⛔ cannot assign to property: 'get_variable' is a get-only property
// unconformed.get_computed = 1  ⛔ cannot assign to property: 'get_computed' is a get-only property
// unconformed.get_private_set = 1  ⛔ cannot assign to property: 'get_private_set' is a get-only property
unconformed.getset_variable = 1
unconformed.getset_computed = 1
```