LoginSignup
24
6

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-14

#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

24
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
6