Protocol ExtensionでOptional型を無闇に使ってハマったこと。

# 要約

この記事はダラダラと長いです。コメント欄の議論がよくまとまっているので、先にそちらを見ていただいたほうがいいかもしれません。
Protocolに宣言されたプロパティと同名でかつ型の違うプロパティをProtocol Extensionで記述すると、それを実装したクラスや構造体に同名で型の違う２種類のプロパティが存在するという状況が起こるというトラップのような仕様のお話です。

# 僕が実際に体験したこと

最近Protocol Oriented　Programmingを勉強して、さっそく業務でProtocol Extensionを意識したコードを書き始めています。ですが、単純な罠にハマって１時間ほど何が原因なのかがわからず困ってしまったので戒めの意味を込めて記事を書いてみました。

今回は例として`Callable`というものを考えます。構造体に実装することで、名前を呼ぶことができる構造体を作ることができます。
特に指定がない場合を考えて、デフォルトの名前として、`"You"`を利用するようにしたいので、Protocol Extensionにて実装しました。

```swift
protocol Callable {
    var name: String { get }
}

extension Callable {
    var name: String {
        "You"
    }
}
```

使い方としてはこんな感じをイメージしておきます。

```swift
struct Taroh: Callable {
    let name = "Taroh"
}
let person = Taroh()
print(person.name) // Taroh
```

```swift
struct NoNamePerson: Callable {
}
let person = NoNamePerson()
print(person.name) // You
```

ここまでは問題がないのですが、ここからが問題です。
`name`プロパティは`get`アクセサしか持たないので、構造体を作ったあとにはその値を変更することができません。
そこで僕は思いつきました。「Optionalにして、後で入れればよいのでは」と。

```swift
struct Taroh: Callable {
    var name: String? = nil
}
```

そうすると、

```swift
var mutableTaroh = Taroh()
mutableTaroh.name = "new man"
```

このように、値を入れることができるようになります。
では、さっそくプロトコルに仕事をしてもらおうと、ジェネリクス関数を作成しました。
実行してみるとこうなるんです。

```swift
func callName<T> (_ sample: T) where T: Callable {
    print(sample.name)
}
callName(mutableTaroh) // You
```

さっき`new man`を入れたばかりなのに、デフォルトの実装のままになってしまっています。
実はこの状態で構造体のプロパティの内容をプリントすると、

```swift
print(mutableTaroh.name) // Optional("new man")
```

正しく値は入っているのですが、なぜかOptional型になっています。

# 事実としてわかっていること

完璧な原因までは特定できてないので、コメント欄にてこの問題をどのように解釈すればいいのか教えていただきたいです。
ここからは自分なりの解釈を書きます。

## Protocol Extensionをやめると、そもそもオプショナル型のプロパティを実装できない

```swift
struct Taroh: Callable {
    var name: String? = nil
}
```
この上記の実装は、デフォルトの名前をProtocol Extensionで`You`になるようにするのをやめると、エラーになります。
つまり、Protocol Extensionをしたことによって、（今思えば）謎の実装が可能になったわけです。

Optionalが悪いのかと思い、試しに`name`プロパティを`Int`や`Int?`の別の型にしてみると、エラーになります。
まずイニシャライザが変わるということと、新しく`new man`と代入したところが型の不一致でエラーになります。

ひとまずここでわかったことは、ここでエラーになっていればProtocol Extension周りで何かがおかしいことに気付けるということです。逆に言うと、`String`から`String?`に型をオプショナルにするだけの変更に変えたことによってこの問題に気づくことができなくなったということがハマった一つの原因として挙げられそうです。

## Optionalで上書きしたことによって、対象のオブジェクトをプロトコルとして見るか、構造体として見るかで値が変わるようになってしまった。

簡単にいうとこういうことなんですが、、

```swift
let taroh = Taroh()
print(taroh.name) // Optional("new name")
let tarohCallable: Callable = Taroh()
print(tarohCallable.name) // You
```

これは納得行きそうな気はしますが、

```swift
struct Taroh: Callable {
    let name = "Taroh"
}
let person = Taroh()
print(person.name) // Taroh
```

上記のケースだと、`Callable`プロトコルにしても値は変わりません。むしろこっちも直感的な挙動ですよね。

## `set`アクセサをプロトコルに宣言するとOptionalで上書きできなくなる

```swift
protocol Callable {
    var name: String { get set }
}
```

問題が起きてる状態で、上記に変更するとProtocolに準拠していないとエラーで怒られます。


# 雑感
結果的になぜこうなるかはここまででわからずじまいですが、とりあえず何も考えずにOptionalで上書きするのは直感的な挙動から大きく外れるのでやめたほうがいいということです。
特に僕がハマったときは、デフォルトの値が空配列だったので、余計に何が起きてるかを特定するのに時間がかかりました。。

そもそも、Protocol Extensionの実態が何なのかということと、アクセサの実態が具体的にどうなっているのかがわかっていない可能性があると感じました。（そこまでわからなくても良さそうな気はするのですが・・・）
おそらく何らかの原因でProtocol Extensionのプロパティと構造体のプロパティが別物として扱われているのだと思っています。


```swift
protocol Callable {
    var name: String { get }
}

extension Callable {
    var name: String {
        "You"
    }
}

struct Taroh: Callable {
    var name: String? = nil
}

struct NoNamePerson: Callable {
}

func callName<T> (_ sample: T) where T: Callable {
    print(sample.name)
}

var mutableTaroh = Taroh()
mutableTaroh.name = "new"
print(mutableTaroh.name)
callName(mutableTaroh)
```

最後に、問題のコードを貼り付けておきます。

```
Apple Swift version 5.4.2 (swiftlang-1205.0.28.2 clang-1205.0.19.57)
Xcode: Version 12.5.1 (12E507)
```