今回は Swift.orgAbout Swift に記載されている Swift の追加機能、そこの最後の項目にあたる 高度な制御構文 について見ていきます。Swift には guarddefer といった特徴的かつ便利な制御構文があるので、その辺りの特徴をしっかり押さえておきましょう。 どうぞよろしくお願いしますね。

————————————————————————— 熊谷さんのやさしい Swift 勉強会 #7

00:00 開始 00:25 guard 05:05 早期 Exit 10:34 前提条件 12:03 Fall through 14:56 for ループでの guard 17:27 do 構文での guard 21:41 do 構文とラベル 26:16 Never 29:56 複数の guard を使う 32:54 precondition 36:22 switch 文と guard 38:12 guard と fallthrough 39:06 オプショナルバインディング 42:44 repeat 48:10 repeat と continue 50:39 質疑応答 53:34 構造化プログラミング 55:01 次回の展望 —————————————————————————

Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #7

今日は、その次の「ガード」「リファー」「リピート」あたりを見ていこうと思います。これだと時間内に終わるかな? 終わりそうなら次に進んでいきたいと思います。では、まずガードからやってみましょう。

このガードがまたいい感じの機能で、一般的には「早期リターン」と呼ばれています。この機能はSwift言語自体も含めて、迅速に処理を抜け出すことを保証するための言語構文です。口で説明しても分かりにくいので、Playgroundで具体的に例を見ていこうと思います。

デバッグを行う際、ガード文を活用する方は多いかと思います。構文自体はとてもシンプルです。例えば、次のように関数を書いてみましょう。この関数内でガード文を活用してみます。

まず、基本的な関数 dividing(by:) を定義します。この関数は整数を引数にとり、ゼロで割ることを回避するためのガード文を含んでいます。

extension Int {
    func dividing(by value: Int) throws -> Int {
        if value == 0 {
            throw CalculationError.dividingByZero
        }
        return self / value
    }
}

この際、エラー処理も活用してゼロで割る操作を防ぐ方法も合わせて見ていきましょう。具体的なエラーとして CalculationError を定義し、特に dividingByZero というエラーを用意します。

enum CalculationError: Error {
    case dividingByZero
}

上記のコードの流れを確認しましょう。もし value がゼロでない場合、通常通り計算が実行されます。しかし、もしゼロで割ろうとすると、エラーをスローするようにしています。

ただし、ガード文を活用すると、よりシンプルかつ明確なコードになります。以下のように書き換えます。

extension Int {
    func dividing(by value: Int) throws -> Int {
        guard value != 0 else {
            throw CalculationError.dividingByZero
        }
        return self / value
    }
}

このように、ガード文を用いることで、条件が満たされない場合に早期に処理を終了させ、下のコードの可読性を高めることができます。ガード文を用いることにより、インデントの増加による読みにくさを解消することが可能です。

もし、このスローで処理が終わることがわかりにくい場合は、エラーハンドリングに慣れていない人のために、以下のようにイメージしてもらえるかと思います。

extension Int {
    func dividing(by value: Int) throws -> Int {
        guard value != 0 else {
            throw CalculationError.dividingByZero
        }
        return self / value
    }
}

この例から分かるように、ガード文を積極的に活用し、可読性の高いコードを目指しましょう。 こういう便宜上リターンが入っているものと思ってもらえればいいかなと思います。ここで大事なことに繋がるんですが、このようなコードを書いたときに、うまくいっていればいいんですけど、例えばこの条件処理の中が何らかの形でコードが長くなってしまったときや複雑な条件があったときに、何らかの事情でエラーを返すタイミングやリターンするタイミングを逃してしまうことがあります。

こうしたときに、この書き方だとエラーが何も表示されず、エラーとして扱われずに計算が進行してしまいます。今回の場合はゼロで割ったときにランタイムエラーが発生するため、それで気づくわけですが、コードによってはもっと後になってから気づくことや、データを壊してから気づくことがあり得ます。それを防ぐのが、ガードの大事な役割となります。

実際にエラーが起こると、「bad instruction division by 0」というランタイムエラーが発生します。例えば、printを使ってみましょう。このように表示された後にランタイムエラーが発生します。本来ならここでエラー処理をしてエラー想定を終わらせるつもりだったのに、printまで行ってしまう場合があります。