前提
上記Youtubeを日本語でざっくり解説します。
※あくまでも私のアウトプットなので間違っていたらご教授いただけますと幸いです。
どのような内容か
以下は、コンパイルエラーが発生しないコードです。
import Foundation
struct Person {
let id = UUID()
let name: String
}
struct Location {
let id = UUID()
let coordinates: (Double, Double)
}
func handle(locations: [Location]) {
let me = Person(name: "Test")
let filtered = locations.filter { $0.id == me.id }
}
以下は、コンパイルエラーが発生するコードです。
import Foundation
struct ID<T>: Equatable {
private let value = UUID()
}
struct Person {
let id = ID<Self>()
let name: String
}
struct Location {
let id = ID<Self>()
let coordinates: (Double, Double)
}
func handle(locations: [Location]) {
let me = Person(name: "Test")
let filtered = locations.filter { $0.id == me.id } // ここでエラー
}
Cannot convert value of type 'ID<Person>' to expected argument type 'ID<Location>'
ここで伝えておきたいことは、今回の動画では コンパイルエラーが発生しないことが問題(ダメ)だよ ってことだと思います。
なぜコンパイルエラーが発生しないことに問題があるのか
以下は先程のコンパイルエラーが発生しないコードと同様
import Foundation
struct Person {
let id = UUID()
let name: String
}
struct Location {
let id = UUID()
let coordinates: (Double, Double)
}
func handle(locations: [Location]) {
let me = Person(name: "Test")
let filtered = locations.filter { $0.id == me.id } // ココに問題がある
}
Swiftでは、異なる型同士での比較はしてはいけません。
簡単に言うと、以下は比較できませんよね。
let int = 0
let string = "0"
if int == string {
}
// 以下はエラー内容
// Binary operator '==' cannot be applied to operands of type 'Int' and 'String'
この決まりがある中で、コンパイルエラーにならなかったコードの以下の部分は、型が違うのにコンパイルエラーが出ていません。
{ $0.id == me.id } // $0は、Location型。 meは、Person型。
// なぜコンパイルエラーにならないのか
// コンパイルするときに$0とmeの型は違うけど、どちらのidもUUID型だから比較OKになってしまう。
これは予測できない動作を引き起こす可能性が出てきてしまうので、今回の動画ではこのコードは書かないでください!ってことになっている。と思われます!
だから、エラーが発生する方のコードのようにして、異なる型同士の比較をできないようにコンパイルエラーを出してくれるようにしよう!
といった感じです。
最後に
エラーが発生する方のコードのようにして、異なる型同士の比較をできないようにコンパイルエラーを出してくれるようにしよう!
先程の引用で上記のようにコンパイルエラーを出すようにすることを幽霊型というらしい。
詳しくは以下を参考