今回は Swift 3 から登場した Never
型で遊んでみたいと思います。
この記事は Never GiveUp というタイトルで Swift Tweets で発表したツイートをまとめたものになります。
Never 型とは
Never
型は fatalError
でも使われる、戻り値がない ことを示す型です。
Swift 2 では @noreturn
属性でしたが、表から『値を返さないこと』が見えにくいため、Swift 3 からは型として明記されることになったみたいです。
/////////////////////////////////////////////////////
//// ⭐️ 生還に期待を寄せてしまうコード (Swift 2)
@noreturn func oneWayTrip() -> Gift? {
}
/////////////////////////////////////////////////////
// ⭐️ 戻り値がないことが明確なコード (Swift 3)
//
func oneWayTrip() -> Never {
// コンパイラーによって、必ず処理が打ち切られることが保証される。
// ERROR: Function with uninhabited return type 'MyNever' is
// missing call to another never-returning function on all paths
}
// この戻り値が Never なので、処理はここまで。
oneWayTrip()
// この行には辿り着き得ない。
print("DONE") // WARNING: Will never be executed
その実態は、列挙子のない列挙型は値化できない性質を応用して『値が存在し得ないから打ち止め』みたいな表現です。実際の定義は、このようにシンプルなものになっています。
enum Never {
}
Never は、つくれる?
それなら『もしかして自分で作れる?』と思ってみたのが今日のテーマです。
実際、列挙子を持たない列挙型を定義しただけで、標準の Never
と同じ動きを見せてくれました。
enum MyNever {
}
func oneWayTrip() -> MyNever {
// ERROR: Function with uninhabited return type 'MyNever' is
// missing call to another never-returning function on all paths
}
// ⭐️ 標準の Never と同じ動作になった
oneWayTrip()
print("DONE") // WARNING: Will never be executed
本当に、値化できない?
ところで本当に値化できないのでしょうか。そう思って標準の Never
にイニシャライザーを拡張して使ってみると、右辺を評価したところ打ち切りに。やはり無理みたいです。
extension Never {
init() {
self = unsafeBitCast((), to: Never.self)
}
}
// ⭐️ やっぱりインスタンス化はできない
let never = Never() // WARNING: Will never be executed
じゃあ、イニシャライザーではなく関数ならどうだろうと試してみたのですけど、やっぱりダメそうですね。関数の呼び出しまでで打ち切りです。
func oneWayTrip() -> Never {
return unsafeBitCast((), to: Never.self)
}
let never = oneWayTrip() // WARNING: Will never be executed
でも、もしかしてオプショナルに包んでみたら…? と試してみたら、できました。オプショナルバインディングで Never
な値が取れました。子供騙しな感は残りますけれど。
func oneWayTrip() -> Never? {
return unsafeBitCast((), to: Never.self)
}
if let never = oneWayTrip() {
print("Success !?", never) // "Success !? \n"
print("Type is", type(of: never)) // "Type is Never\n"
}
それなら Any
型は…? と試してみると、大丈夫みたい。入れ物は Any
ですが、オプショナルと違ってすぐ使えるし、キャストで正真正銘 Never
な値になります。
func oneWayTrip() -> Any {
return unsafeBitCast((), to: Never.self)
}
// ⭐️ 型は Any
let never = oneWayTrip()
print("Type is", type(of: never)) // "Type is Never\n"
print("Is Never: ", never is Never) // "Is Never: true\n"
// ⭐️ キャストすると純粋な Never 型の値として使える。
let a = never as! Never
print(a) // "\n"
ジェネリック型だとどうでしょう。何かしらの形で Never
を指示しないといけないものの、型推論も期待できるので、Never
を取る関数にそのまま渡せたりします。
func oneWayTrip<N>() -> N {
return unsafeBitCast((), to: N.self)
}
let never: Never = oneWayTrip()
print("Never", never) // "Never \n"
print("Type is", type(of: never)) // "Type is Never\n"
// ⭐️ とりあえず、スマートに Never 型を受け取るところまではできた。
func challange(_ something: Never) {
print(something)
}
// ⭐️ 奇跡の生還
challange(oneWayTrip())
// ⭐️ 処理が継続
print("DONE") // "DONE"
おしまい
今回はここまで。Never
を自作したり、列挙子を持たない列挙型を値化したりして遊んでみました。これによってどんな世界が広がるのかは分からないですけど、とりあえず面白かったです。
以上、ありがとうございました!