protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: Container {} struct Stack<Element>: Container { // original Stack<Element> implementation var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // conformance to the Container protocol mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item,C1.Item: Equatable{ // Check that both containers contain the same number of items. if someContainer.count != anotherContainer.count { return false } // Check each pair of items to see if they're equivalent. for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // All items match, so return true. return true } var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") var arrayOfStrings = ["uno", "dos", "tres"] if allItemsMatch(stackOfStrings, arrayOfStrings) { print("All items match.") } else { print("Not all items match.") } //All items match.
https://docs.swift.org/swift-book/LanguageGuide/Generics.html
上記ページのGeneric Where Clausesの部分のサンプルコードを見ていて頭に浮かんだただの興味なのですが質問させてください。このコードの動き自体はなんとか
理解できました。Containerプロトコルの定義内の連想型Itemには特に型制約は無く、
allItemsMatch(::)関数のGeneric Where Clausesの
C1.Item: Equatable
によりcontainerの要素の型を「Equatableプロトコルに準拠した型」と指定しています。
まあこれで動くのですが、どうせcontainerの要素の型を「Equatableプロトコルに準拠した型」に
指定するのなら、Containerプロトコルの定義内の連想型Itemを
protocol Container { associatedtype Item:Equatable ...
のように指定した方が、自然な気がしました。
そこでallItemsMatch(::)関数のGeneric Where Clause部分の
C1.Item: Equatable
を消して、代わりにContainerプロトコルの定義内の連想型Itemを
protocol Container { associatedtype Item:Equatable ...
のようにしても問題無く動くのではないか、と思いやってみたのですが(以下のコード)、
protocol Container { associatedtype Item:Equatable mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: Container {} struct Stack<Element>: Container { // original Stack<Element> implementation var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // conformance to the Container protocol mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item{ // Check that both containers contain the same number of items. if someContainer.count != anotherContainer.count { return false } // Check each pair of items to see if they're equivalent. for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // All items match, so return true. return true } var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") var arrayOfStrings = ["uno", "dos", "tres"] if allItemsMatch(stackOfStrings, arrayOfStrings) { print("All items match.") } else { print("Not all items match.") }
error: type 'Stack<Element>' does not conform to protocol 'Container' error: type 'Array<Element>' does not conform to protocol 'Container'
上記のようなエラーが出ました。つまりContainerプロトコルを変えたことにより、Array型、Stack<Element>型
がContainerプロトコルに準拠できなくなった、ということらしいのですが、これを解決するためにはArray型、Stack<Element>型の定義内で、連想型Itemを「Equatableプロトコルに準拠した型」に指定する必要がある、
ということになるのでしょうか。ただArray型もStack<Element>型もジェネリック型なので、連想型Itemと紐付く型は型引数のElementになると思います。ですので、型引数Elementに対して型制約(Equatableプロトコルに準拠しなければならない)を課す必要がある、ということになるのでしょうか。
ここでStack<Element>型は、Stack<Element>の定義内で、
struct Stack<Element:Equatable>: Container { // original Stack<Element> implementation ....
のようにすればエラーは消えました。結局Stack<Element>型は(組み込み型では無く)独自の型なので、このような型制約の指定も、開発者が好きなようにすればいいような気がしますが、Array型は組み込み型なので、その定義をいじるのは、普通しないような気がします。(いじるとしてもいじり方もよくわからないし、それよりもいじって他で問題が出ないか、そちらの方が問題が大きいような気がする。ただこのコードに関して言えば、Stack<Element>もArray<Element>も似たようなものなので、ArrayElement:Equatableとすれば良いだけで、特に問題は無いような気もしますがよくわかりません。)
ということで、Array型に関しては定義はそのままで、例えばエクステンションなりでエラーを出なくする方法はあるでしょうか?
エクステンションなりでエラーを消そうとするのも普通はやらないのかもしれませんが、あくまで理論的な話として何か方法があるのか、気になったので質問させていただきます。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/02/10 02:07 編集
2020/02/10 04:38
2020/02/11 00:36