LoginSignup
2
0

More than 5 years have passed since last update.

[Swift] Protocolがrequireしているかどうか、それが問題だ。

Posted at

概略

Swiftにおいて、protocol extensionで関数を実装したとしても、そのprotocolがその関数を必要とすると宣言しているか否かで、結果が変わることがあるよ。
(あとから気付いたのですが、別の方の記事の要約のようになってしまいました。詳しく知りたい方は当該記事をご覧ください。)

百聞は一見にしかず

この記事で言いたいことの全ては下記のコードが語ってくれます:

protocol P {
  func f()
}

protocol Q {
  // こっちは、からっぽ。
}

extension P {
  func f() { print("P") }
}

extension Q {
  func f() { print("Q") }
}

struct SP: P {
  func f() { print("SP") }
}

struct SQ: Q {
  func f() { print("SQ") }
}

SP().f() // "SP"と表示
SQ().f() // "SQ"と表示

func g<T>(_ p:T) where T:P { p.f() }
func g<T>(_ q:T) where T:Q { q.f() }

g(SP()) // "SP"と表示
g(SQ()) // "Q"と表示!!

Swiftに精通している方にとっては常識なのかもしれませんし、よく考えると当然の動作とは言えます。

素人による解説

SP().f() // "SP"と表示
SQ().f() // "SQ"と表示

これは期待を裏切らない動作ですね。SPSQも関数f()を実装していることは分かっているので、それぞれの構造体で実装されているメソッドが呼ばれます。
それでは次を見てみましょう。

func g<T>(_ p:T) where T:P { p.f() }
func g<T>(_ q:T) where T:Q { q.f() }

g(SP()) // "SP"と表示
g(SQ()) // "Q"と表示!!

この動作の違いこそ、プロトコルが必要とする要件にfunc f()が宣言されているかどうかが生み出すものです。

プロトコルPにはfunc f()が必要と宣言されています。即ち、本来であればPに準拠する構造体(クラスや列挙型などもね)が実装の責任を負うことになります。ところで、関数gにとって引数pPに準拠していることが判明しています。ということは、pはメソッドfunc f()を実装しているはずです。だからこそ、pそのものに実装されているfunc f()を安心して呼び出せます1

一方、プロトコルQfunc f()が必要とは宣言されていません。なので、関数gにとって、引数qfunc f()を実装しているかどうかなんて分かりません。むしろ、実装していないと考えるのが自然(安全)でしょう。となると、qに対してfunc f()を呼び出そうとする危険な行為はせず、protocol extensionにあるfunc f()を呼ぶことになるでしょう。



  1. SPからfunc f()を消すと、Pのprotocol extensionにあるfunc f()が呼び出されます。ここでの疑問は、“Pのprotocol extensionにfunc f()が実装されていたとしても、SPfunc f()が実装されていなかったら、関数gpに対してfunc f()を呼び出そうとした時に、なぜクラッシュしないのか?”ということです。これは予想ですが、コンパイラによって、SPに暗黙的にfunc f()を作るというようなこと(protocol extensionのfunc f()をコピーするまたはそれへ転送するような形)をしているのではないでしょうか。そうでなければ、func f()を動的に探索する際の選択肢にprotocol extensionのfunc f()を加えているということになりますが…。はたして?正解はGitHub/apple/swiftで!(丸投げ) 

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0