質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

711閲覧

ProtocolExtensionをor演算子で制約したい

Iwasho

総合スコア31

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/09/01 21:40

前提・実現したいこと

ProtocolExtensionにおいて、どちらかのプロトコルに適合していれば有効になるようにしたいです。
素直に別々に書くしかないのでしょうか?

該当のソースコード

protocol SomeProtocol { } //こちらはできる extension SomeProtocol where Self: HogeProtocol & FugaProtocol { } //こちらはエラーになる extension SomeProtocol where Self: HogeProtocol || FugaProtocol { }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

■ 結論から

論理的に無理が出てくるので、実現できないようになっていると思われます。

■ 現実問題として A or B だとコードが書けない

プロトコル拡張するということは、そこでメソッドなどの機能を実装したいと思うのですけれど、このとき今回の例であれば「SelfHogeProtocolFugaProtocol のどちらになるかわからない」ということは「SelfHogeProtocol で規定された機能を持っていないかもしれないし、FugaProtocol で規定された機能を持っていないかもしれない」状況になります。

なので、例えば次のように各プロトコルに機能が規定されていたとします。

swift

1protocol SomeProtocol { 2 func some() 3} 4 5protocol HogeProtocol { 6 func hoge() 7} 8 9protocol FugaProtocol { 10 func fuga() 11}

こうした時に、実際に SomeProtocol に機能を拡張しようとすると、次のように「どちらかのプロトコルに該当していた場合」という制約に無理が出てくることが実感できると思います。

extension SomeProtocol where Self: HogeProtocol | FugaProtocol { func someWhereHogeOrFuga() { // 次のコードは `SomeProtocol` が持っている機能なので問題なし self.some() // 次のコードは `Self` が `FugaProtocol` だけに準拠していたとすると // `hoge` メソッドを持っていないため、呼び出しを保証できず、コンパイルを通すことができない。 self.hoge() // かといって次のコードも `Self` が `HogeProtocol` だけに準拠していたとすると // `fuga` メソッドを持っていないため、呼び出しを保証できず、コンパイルを通すことができない。 self.fuga() } }
■ A と B、それぞれを想定したコードを書かざるを得ない

そのため、実質的にコードを書くことができない状況になります。そうして結局行き着くところとしては、必然的に次のようになってきます。

  • HogeProtocol で提供する機能でコードを組み立てる時には where Self: HogeProtocol になる
  • FugaProtocol で提供する機能でコードを組み立てる時には where Self: FugaProtocol になる
  • HogeProtocolFugaProtocol の機能が必要なら where Self: HogeProtocol & FugaProtocol

HogeProtocol または FugaProtocol という表現が必要になるときは、仮に HogeProtocol | FugaProtocol と書けたとしてもコードが立ち行かなくなって、結局のところは次のように HogeProtocol の場合と FugaProtocol の場合のそれぞれでコードを書いていくことになると思います。

swift

1extension SomeProtocol where Self: HogeProtocol { 2 func someWhereHogeOrFuga() { 3 4 // 次のコードは `SomeProtocol` が持っている機能なので問題なし 5 self.some() 6 7 // 次のコードは `Self` が `HogeProtocol` に準拠しているので 8 // `hoge` メソッドを持っていて、コンパイルを通すことができる。 9 self.hoge() 10 } 11} 12 13extension SomeProtocol where Self: FugaProtocol { 14 func someWhereHogeOrFuga() { 15 16 // 次のコードは `SomeProtocol` が持っている機能なので問題なし 17 self.some() 18 19 // 次のコードは `Self` が `FugaProtocol` に準拠しているので 20 // `fuga` メソッドを持っていて、コンパイルを通すことができる。 21 self.fuga() 22 } 23}

このように、実装するときはそれぞれに対してコードを書いていく必要がありますけれど、使うときには HogeProtocol または FugaProtocol のどちらかに準拠していれば(当たり前ですけれど)someWhereHogeOrFuga メソッドを使えます。

swift

1struct A : SomeProtocol, HogeProtocol { 2 3 func some() {} 4 func hoge() {} 5 6 func test() { 7 8 // `HogeProtocol` に準拠しているだけでも 9 // `someWhereHogeOrFuga` が使える。 10 someWhereHogeOrFuga() 11 } 12} 13 14struct B : SomeProtocol, FugaProtocol { 15 16 func some() {} 17 func fuga() {} 18 19 func test() { 20 21 // `FugaProtocol` に準拠しているだけでも 22 // `someWhereHogeOrFuga` が使える。 23 someWhereHogeOrFuga() 24 } 25}
■ A and B、両方を想定したコードを書かないといけない場合も

ただし、今のままだと HogeProtocolFugaProtocol の両方に準拠している場合に where Self : HogeProtocolwhere Self : FugaProtocol のどちらで実装した someWhereHogeOrFuga メソッドを使えばいいか判断できずに呼び出せないので、必要に応じて両方に準拠させた型で独自に実装するか、または次のようにあらかじめ where Self : HogeProtocol & FugaProtocol の場合として、両方に準拠していた場合の someWhereHogeOrFuga を規定しておくことで対応したりします。

swift

1extension SomeProtocol where Self: HogeProtocol & FugaProtocol { 2 func someWhereHogeOrFuga() { 3 4 // 次のコードは `SomeProtocol` が持っている機能なので問題なし 5 self.some() 6 7 // ここでは `Self` が `HogeProtocol` と `FugaProtocol` の両方に準拠しているので 8 // それらの機能を使ってコードを組み立てることが可能です。 9 if Bool.random() { 10 self.hoge() 11 } 12 else { 13 self.fuga() 14 } 15 } 16} 17 18struct C : SomeProtocol, HogeProtocol, FugaProtocol { 19 20 func some() {} 21 func hoge() {} 22 func fuga() {} 23 24 func test() { 25 26 // `HogeProtocol` と `FugaProtocol` の両方に準拠している場合も 27 // それを想定した `someWhereHogeOrFuga` を定義したことで、それを呼び出せるようになります。 28 someWhereHogeOrFuga() 29 } 30}
■ 所感

こうして見ていって面白いのが、必然的に Self : HogeProtocolSelf : FugaProtocolSelf : HogeProtocol & FugaProtocol の3パターンで書くことになって、Self : HogeProtocol | FugaProtocol みたいな書き方は実質コードを書くことができず、結局のところその3つのパターンで補い合って書くことになる。

そんなところが見えてくると『素直に個別に書くしかない』ことが、自然なものとして素直に受け入れられるようになってくるかもしれません。

投稿2021/09/02 07:02

編集2021/09/02 07:05
TomohiroKumagai

総合スコア441

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Iwasho

2021/09/02 07:15

丁寧に解説していただきありがとうございました!
guest

0

ワークアラウンドとして

swift

1protocol PiyoProtocol { 2 // extensionに必要なプロパティ、メソッドを列挙 3} 4 5extension HogeProtocol: PiyoProtocol {} 6extension FugaProtocol: PiyoProtocol {} 7 8extension SomeProtocol where Self: PiyoProtocol { 9 // 10}

とする方法があります。

投稿2021/09/02 00:31

MasakiHori

総合スコア3391

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Iwasho

2021/09/02 04:55

Extension of protocol 'XXXXProtocol' cannot have an inheritance clauseとエラーが表示されます。 プロトコルの拡張でプロトコルを適合できないそうなのですが...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問