Edited at

Never GiveUp というタイトルで Swift Tweets で発表しました。 #swtws

More than 1 year has passed since last update.

#swtws Never GiveUp.001.png

今回は 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 を自作したり、列挙子を持たない列挙型を値化したりして遊んでみました。これによってどんな世界が広がるのかは分からないですけど、とりあえず面白かったです。

以上、ありがとうございました!

#swtws Never GiveUp.002.png