今回からは The Basics
の Assertions and Preconditions
の項を見ていきます。こちらもこれまでに幾度か登場してきた話題ですけれど、その初歩的なところを改めて眺めていけたらいいなと思っています。どうぞよろしくお願いしますね。
今回は参加者の一般参加者を募って、ゆめみの外の人たちも訪れての開催になる見通しです。
———————————————————————————— 熊谷さんのやさしい Swift 勉強会 #190
00:00 開始 00:11 前回の Error に関する話の補足 00:35 Error に関する技術ブログ 03:12 気を配っていけることの大切さ 04:39 今回の展望 05:41 Assertion の日本語訳は? 06:57 "電脳" は日本由来の言葉らしい 09:25 "前提条件" は自然な日本語 10:02 実行時に行われる検査 11:00 必要な条件が満たされていることの確認 12:30 条件に合致しないときのメッセージを添えられる 13:56 表明の記載方法 15:44 ランタイム時の検査がランタイム前にも活きる 16:52 最適化によって除れるのも魅力 18:02 将来はコンパイル時に検査する可能性も? 19:23 仮定や期待値をコードに埋め込める 21:24 例外処理との使い分け 23:22 正解よりも最善の手を探す心構えで 24:36 クロージング ————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #190
では、勉強会を始めていきます。まずは前回お話ししていたエラー周りの話についてです。これについて調べていたら、たまたま詳細にまとめた良い感じのブログを見つけました。そのブログを軽く紹介しつつ、エラーハンドリングの話に入っていきたいと思います。
そのブログは、自分が話したいと思っていた話題を精密にまとめていますので、おさらいとしても非常に良さそうです。詳しくはここでは見ていかないですが、軽く紹介しておきたいと思います。誰のブログかというと、「STMN Inc.」のMokissという名前で書かれているようです。
ここでは、SwiftのエラープロトコルやNSエラー、LocalizedError、localizedDescriptionについて非常に細かく説明されています。このブログを見れば、Swiftのエラープロトコルの仕組みやエラーハンドリングがどう行われるのか、あるいはローカライズの提供方法など、前回話した内容をさらに深く理解するのに役立ちます。もし前回の勉強会で話が分かりにくかった人がいれば、このブログを見ると良いおさらいになるでしょう。
さて、今回はアサーションとプレコンディションについて見ていきます。無理やり日本語に訳してみると、アサーションは「表明」となりますが、どうでしょうかね。日本語でアサーションという方が意外と馴染みがあるかもしれません。アサーションは条件を入れて、その条件が満たされなかったらエラーを出すという仕組みです。これにより、早い段階で間違いに気づけるわけです。
Swiftの公式ドキュメントによると、アサーションとプレコンディションは実行時に行われる検査です。これらはコードの先へ進むより前に指定された条件が満たされているか確認するためのものです。アサーションやプレコンディションの関数が入っていたら、これは実行時に条件をチェックするためにプログラマーが入れ込んでいると理解すると良いでしょう。
具体的には、assert
やprecondition
といった関数を使います。例えば、ある条件式があって、func divide(_ numerator: Double, by denominator: Double) -> Double
という関数を考えたときに、この関数がdenominator
で割り算をする場合、assert(denominator != 0, "Denominator must not be zero")
というアサーションを入れることで、0で割ることがないように検査できます。
これらの関数は条件が真の場合には通常通りに実行されますが、偽の場合にはプログラムを終了させるというのが基本の動作です。終了する際にはメッセージを添えることができるので、どのような事情でプログラムが実行を継続できなかったのかを通知できます。
アサーションやプレコンディションは、デバッグ時に非常に有用ですし、コードレビューの段階でもレビューアーに書いた条件がしっかりと意識されるメリットがあります。
それでは、アサーションとプレコンディションを実際にどう書くのか見ていきます。例えば、func divide(_ numerator: Double, by denominator: Double) -> Double
という関数があったとき、以下のように書きます。
func divide(_ numerator: Double, by denominator: Double) -> Double {
assert(denominator != 0, "Denominator must not be zero")
return numerator / denominator
}
このようにすることで、denominator
が0でないことを確認し、問題があればプログラムを終了させることができます。これにより、コードが正しく動いているかを早期に検査できるようになるわけです。
今回はここまでとしますが、次回も引き続きエラーハンドリングやその他のSwiftの言語仕様について詳しく見ていきたいと思います。 とりあえずアサートで書いておいて、ここにバリューを入れると「割り算は0では割れないよ」と書いてあげたほうが分かりやすいのかな。例えば、型にInt
型とかを指定したと仮定すると、以下のように書くことができます:
assert(value != 0, "割り算は0では割れません")
こうすることによって、アサーションまたはプレコンディションを設定できます。この条件を満たしたときには何事もなかったかのように次へ進み、条件を満たさなかった場合にはプログラムを終了します。このメッセージを作っておくことで、ランタイムでの検査を前提にしているコードですが、実行前にもプログラマーにその意図が分かりやすくなります。
もちろん、実際に落ちるのは実行時になってからですが、それでもこういった設定があることで、0での除算がダメというルールを明示できます。アサートがない場合、一般的なプログラミング言語では0で割るとプログラムがクラッシュしますが、このように明示しておくことで予期せぬクラッシュを防ぐことができます。
さらには、最適化をかけることによって、3行目のコードがなかったかのように扱われる場合もあります。コメントと異なり、最適化をかけると本当に落としてくれるという点が重要です。コメントで記述されている内容は最適化されても通知されることはありませんので、こうしたアサートやプレコンディションの存在意義が際立ちます。
また、将来的にはコンパイル時に値が決まる属性がSwiftに搭載される可能性があり、その場合はランタイムチェックをしなくても、ビルド時に前提条件をチェックしてくれるかもしれません。これが実現すれば、プレコンディションやアサーションの価値がさらに高まるでしょう。
表明や前提条件を使うことで、プログラマーがイメージしていた前提をコードの一部として埋め込むことができます。これにより、開発時のミスや間違った前提を検出するのに役立ちます。
前提条件が入る場所によっても影響が変わります。たとえば、divide(by value: Int)
関数があるとして、次のようにプレコンディションを設定した場合:
func divide(by value: Int) {
precondition(value != 0, "値は0であってはなりません")
// ... 実際の処理
}
これにより、関数の呼び出し側が0を渡してくるはずがないという主張ができます。もし、この関数を呼び出す前にvalue
に0が代入されるコードがあった場合、そのコードが間違っていることが分かります。 承知しました。文字起こしのテキストを提供してください。それを元に文体を整えて、読みやすい文章に仕上げます。 かしこまりました。文字起こしのテキストを与えてください。それを元に、文体を整え、読みやすい自然な文章にします。 これがない場合だと、ゼロが入っていても論理的に間違っていると言えます。ただ、そこまで厳密には言っていませんので、Aにゼロが入っていてもおかしくないということです。質問をいただいた内容に関連して、例外処理と表明、前提条件の使い分けについて話しましょう。
これらは、コンパイルタイムでの除外やエラーの分類といった細かい規定が存在しますので、軽く話した後、次回以降に詳細を説明したいと思います。
まず、表明についてですが、詳しくはこれから読み進めてみないと正確には言えません。現在、理解が浅い部分もありますが、表明はリリースビルドでは除外されるため、デバッグ段階においてデバッグ情報として埋め込むような使い方をします。
前提条件はリリースビルドにも持ち込むことができます。条件が間違っている場合には、正確にエラーを出すことができるため、その後の処理で誤った計算を防ぐ役割があります。エラーハンドリングはリカバリの可能性を含むもので、条件を満たさなかった場合にその理由を呼び出し元に返し、その元がエラーに対してどのように対応するかを決めるという性質があります。
これらの特徴を踏まえ、適切に使い分けることが重要です。最適な選択肢を見つけ、状況に応じて対応することが大切です。正解を追い求めるのではなく、その場で最善と思われる対応を選びましょう。
エラーハンドリングについては、以前の勉強会で詳しく見てきましたので、関連動画が公開されたらチェックすると良いでしょう。表明と前提条件については次回以降にゆっくりと考察していきます。
今日はここまでにしたいと思います。お疲れ様でした。ありがとうございました。