[Swift] classでもselfに代入したい！

# 目標

```Swift
init(_ otherInstance: Self) {
  self = otherInstance
}
```
というようなことを`class`でもしてみたいけど、どうしたらできる…？
(想定: Swift 5現在)


# はじめに: `self`への代入ができるのはどんなとき？

## 構造体(struct)

次のように、イニシャライザや`mutating func`の中で`self`に代入をすることができます：

```Swift
struct S {
  init() {}
  
  init(_ otherInstance: S) {
    self = otherInstance
  }
  
  mutating func replace(with otherInstance: S) {
    self = otherInstance
  }
}

let s1 = S()
let s2 = S(s1)
var s3 = S()
s3.replace(with: s2)
```


## 列挙型(enum)

列挙型も構造体と同様です：

```Swift
enum E {
  case a, b
  
  init() {
    self = .a
  }
  
  init(_ otherInstance: E) {
    self = otherInstance
  }
  
  mutating func replace(with otherInstance: E) {
    self = otherInstance
  }
}

let e1 = E() // .a
let e2 = E.b
var e3 = E(e2) // .b
e3.replace(with: e1) // .a
```


## クラス(class)では…？

クラスで同様のコードを書いてみると…

```Swift
class C {
  init() {}
  
  init(_ otherInstance: C) {
    self = otherInstance // ⛔️ error: cannot assign to value: 'self' is immutable
  }
  
  func replace(with otherInstance: C) {
    self = otherInstance // ⛔️ error: cannot assign to value: 'self' is immutable
  }
}

```

`self`がimmutableだから代入できないと怒られてしまいました…。
Swiftの`class`における`self`は(`struct`や`enum`のそれと違って)、実体としてのインスタンスを指し示すもの**ではなく**、実体を参照する(C言語的にいえば)ポインタを指し示すものなので、`self`自体は常にimmutableなのです[^class-mutating]。

[^class-mutating]: 逆にプロパティの書き換えは、インスタンスを参照するポインタの値が書き変わるわけではないので、メソッドの宣言などで`mutating`というキーワードが不要ということになります。



# どうしても`class`でも`self`に代入したい

　前述のように単純なコードでは無理ですが、実は、コードをこねくりまわせば`class`においても`self`に代入ができるのです。…正確には`self`に代入したかのような動作をするコードを書くことができるのです。
　どうするのかというと、前項には登場しなかったアレ…そう、プロトコルを使うのです。
　まずは、プロトコルのコードをみてみましょう：

```Swift
protocol P {}
extension P {
  init(otherInstance: Self) {
    self = otherInstance
  }
}
```

このように、プロトコルにおいても`self`への代入が許容されているのです。
あとは、クラスをこのプロトコルに準拠させて、このプロトコルで定義されているイニシャライザを呼び出せば…

```Swift
class CP: P {
  convenience init(_ otherInstance: CP) {
    self.init(otherInstance: otherInstance as! Self)
  }
}
```

できました！


## 注意点

上記のコードでは`as! Self`という強制型変換が必要になります。要するに`CP`のサブクラスでこのイニシャライザを呼んだ時の安全性が確保されません。なので、`CP`を`final class`として宣言したり、プロトコル`P`も`private`で宣言したりすることが望まれます。


# 使用例

## 1. クラスクラスタ

ちょうどいい例がGistにありました: 
https://gist.github.com/JadenGeller/1f4acd5cd07d4642c246d87ca25bd3e0



## 2. 同一入力に単一インスタンス

```Swift
// 生成されたインスタンスを格納
private var _instances: [Int: MyClass] = [:] 

// selfへの代入を可能にするためのプロトコル
private protocol _MyClass {}
extension _MyClass where Self: MyClass {
  fileprivate init(_integer integer: Int) {
    if let instance = _instances[integer] {
      self = instance as! Self
    } else {
      self.init(__integer: integer)
      _instances[integer] = self
    }
  }
}

// 今回メインのクラス
// "designated initializer"は外部から呼べないようにし、
// "convenience initializer"の内部でプロトコルのイニシャライザを呼び出す
public final class MyClass: _MyClass {
  public let integer: Int
  
  fileprivate init(__integer integer: Int) {
    self.integer = integer
  }
  
  public convenience init(_ integer: Int) {
    self.init(_integer: integer)
  }
}

print(ObjectIdentifier(MyClass(0)) == ObjectIdentifier(MyClass(0))) // -> true
print(ObjectIdentifier(MyClass(0)) == ObjectIdentifier(MyClass(1))) // -> false
print(ObjectIdentifier(MyClass(1)) == ObjectIdentifier(MyClass(1))) // -> true
```


# おわりに

いかがだったでしょうか。
`class`でも`self`に代入したい場面というのはそこまで多くはないですが、ゼロではないはずです。方法が存在するということを知っておくだけでも、今後どこかで役に立つかもしれません。…というか役に立つといいなと思っています。