前提・実現したいこと
ProtocolExtensionにおいて、どちらかのプロトコルに適合していれば有効になるようにしたいです。
素直に別々に書くしかないのでしょうか?
該当のソースコード
protocol SomeProtocol { } //こちらはできる extension SomeProtocol where Self: HogeProtocol & FugaProtocol { } //こちらはエラーになる extension SomeProtocol where Self: HogeProtocol || FugaProtocol { }
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
■ 結論から
論理的に無理が出てくるので、実現できないようになっていると思われます。
■ 現実問題として A or B だとコードが書けない
プロトコル拡張するということは、そこでメソッドなどの機能を実装したいと思うのですけれど、このとき今回の例であれば「Self
が HogeProtocol
と FugaProtocol
のどちらになるかわからない」ということは「Self
に HogeProtocol
で規定された機能を持っていないかもしれないし、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
になるHogeProtocol
とFugaProtocol
の機能が必要なら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、両方を想定したコードを書かないといけない場合も
ただし、今のままだと HogeProtocol
と FugaProtocol
の両方に準拠している場合に where Self : HogeProtocol
と where 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 : HogeProtocol
と Self : FugaProtocol
と Self : HogeProtocol & FugaProtocol
の3パターンで書くことになって、Self : HogeProtocol | FugaProtocol
みたいな書き方は実質コードを書くことができず、結局のところその3つのパターンで補い合って書くことになる。
そんなところが見えてくると『素直に個別に書くしかない』ことが、自然なものとして素直に受け入れられるようになってくるかもしれません。
投稿2021/09/02 07:02
編集2021/09/02 07:05総合スコア441
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
総合スコア3391
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/09/02 07:15