https://youtu.be/cUj3Llw5t8g
このところは The Basics
の オプショナル
について眺めていっています。これまではオプショナルの概念的なところとそこから派生した余談みたいな感じで、今回それを具体的に使っていく方法について見ていきます。夏季休暇を挟んで少し間の空いたのでこれまでの内容で忘れがちなところもありそうですけれど、今回から初歩に再び戻る感じですので、話を進めていく中でこれまでに話したことも含めて思い出せていけたらいいなと思ってます。よろしくお願いしますね。
————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #148
00:00 開始 00:10 今回の展望 01:13 非オプショナルな型と nil とを比較できる必要性は? 06:01 全ての値が nil と比較できることについての考察 13:58 オプショナル型のサブタイプを考慮した等価比較 16:36 等価演算と nil の可能性 20:42 nil リテラルとの等価比較で警告を抑える 21:58 セミコロンによる行末の明記 22:21 括弧で意図を明記できる場面もある 24:03 扱う値が比較できないオプショナルに向けた等価比較演算子 26:34 クロージングと次回の展望 —————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #148
では、始めていきますね。今日はオプショナルの話の続きです。これまでオプショナルに関しては概念的な説明をしてきました。今スクリーンに映しているように、「オプショナルってどういうものか」とか、少し難しい部分もありましたが、「オプショナルってこんなものだよね」という基本的な理解を深める話でした。今日からは、実際にオプショナルを使っていく話に移ります。
今日の重要なポイントですが、まぁ、オプショナルは全てが大事とも言えますね。それでは、もう少し具体的なところからお話ししていこうと思います。
例えば、全てのインスタンスに対して nil
との比較演算ができることについて話しました。画面に表示されている例では、例えばオプショナル型でもそうでなくても、nil
と等価比較や不等価比較ができるという話ですね。この仕組みについて見てきましたが、詳細な仕組みは省略します。
型安全の観点では、nil
が入るはずがないものと nil
を比較する必要があるかどうか、と考えることがあります。比較する必要がないところで比較してエラーになれば十分という考えもありますが、現実にはどうでしょうか。オプショナルじゃない値と nil
を比較して嬉しい場面ってありますか?例えば、オプショナルチェーンが続いた先の値を比較するといった状況でしょうか。
オプショナルチェーンにより、型推論で実際の型が関与しなくても良くなっているけれど、オプショナルになっている場合もありますね。オプショナルをずっと外していって、最終的に数値なのか、nil
じゃないのか確認することなどです。
オプショナルじゃない値と nil
を比較する逆のパターンも考えてみましょう。例えば、オプショナルチェーンが絡んでくる状況。この時、オプショナルチェーンは別の話題になるのかもしれませんが、nil
の比較とはまた違うのかなという気もします。実際に試してみましょう。
オプショナルじゃないものでも nil
と比較ができるのはどうなのか。オプショナルチェーンの例として、if let
がオプショナルチェーンの一部ではありますが、nil
の比較とは異なるようです。
一般的に、単体で nil
を比較することはあまり無いことですが、警告で済む場合もあります。これはビルドエラーというよりも、警告レベルで「間違っているかもしれない」と示しているに過ぎません。
オプショナルでなくても nil
と比較できるのは少し怖い部分もありますが、利用価値はあまり見当たりません。スウィフトがこのようにビルドエラーにしない理由は不思議です。常に結果が false
になるので、書いてしまうことが多くないようですが。
総じて、オプショナルと比較できるということは、結果的には nil
とも比較できることを意味します。そういう設計上の感触なのかもしれませんね。 いずれにしても、一生懸命考えていかないといけない場面もあるのだと思います。理由を探さなければいけない場面というのが、今お話を聞いてなんとなくわかりました。たとえば、ここで警告が出るというのをちょっと話し始めるときに、頭から完全に抜けていたのです。警告が出るということは、普通は何か問題があるか、推奨されていないという形になるのです。
Swiftの警告の場合、間違っているかもしれないけれど、そこで何かというと、コードを書くのがしんどくなることがあります。エラーではなくて警告という場合もあるのです。今の説明が少し変かもしれないですが、警告にはそういった意味合いもあるようです。
たとえば、関数や色々なコードがあるときに、試していく中でうまく関数が動かなかった場合、その状況をどう見るかということも出てきます。こういった手っ取り早い方法として、return
文で挟むと、その先はコードが実行されなくなります。基本的にコードが間違っている場合、警告が出るものです。たとえば、ここでコンパイルフェーズのエラーの問題もあるかもしれません。
リターンに続く評価式が公表されると言われているかもしれませんが、重要ではないので置いておきます。たとえば、void
型を返す関数でリターンを書くと、void
を返すわけです。Swiftの行末にセミコロンがなくていい場合がありますが、行末があると解釈できないと、公文エラーとして扱われます。これが安心感のある場所ですが、警告だからこのまま動いてしまうと予期せぬ動作が発生することがあります。
途中にリターンを埋め込んで処理を一旦打ち切って要素を確認する場合もあります。文法的には曖昧だが、エラーにするほどではないので警告にすることがあります。例えば、オプショナルではなくても警告として残しておく場合もあります。オプショナルでない値と比較した場合に、警告としてパスさせることもあります。
これが、バリューにオプショナルを使うときの例ですが、オプショナルではない値と比較できるのおかしいのではないかという指摘もあります。バリューがオプショナルで、比較する場合は問題にならないこともあります。したがって、オプショナルでない値はnil
と比較できないが、そう書いてあっても警告としてパスさせておくという解釈も可能です。警告が出ている場所を踏まえた解釈が必要です。たとえば、オプショナルでバリューを定義するという方法ですね。 例えば Int
オプショナルです。そこに値を設定して、それを値として扱うとします。このとき、警告もなくコンパイルが通りますね。このときにはまた違う演算子がオーバーロードされて、異なる演算子が適用されます。両辺がオプショナルとみなされるタイプの演算子が働くことになります。これはなぜかというと、まず Int
型は Int
オプショナルのサブタイプという仕様が働きます。Int
型は Int
のオプショナル型としても扱えます。だから例えば var value1: Int? = value
が成り立ちますよね。
18行目のように、Int
型と Int
オプショナルの関係がこういう仕様によって関わってきます。その上で、等価比較演算子の Swift における意味としては、Int
のオプショナルの等価比較だったときには、値があった場合、その値と実際に一致するかを判断する材料になります。仮に value2
が非オプショナルだったとすると、値があるのでその値と value
の値を比較します。このとき、オプショナルとして扱わないで比較した場合、コンパイルエラーになります。しかし、Int
型は Int
オプショナルのサブタイプなので、Int
オプショナルとして扱って両辺をオプショナル型として考え、一致するかを判断します。
ちなみに、両方 nil
だったら true
ですよね。そうです、両方 nil
なら true
になります。ただし、両方 nil
になるためには両方ともオプショナルである必要があります。なので value1
と value2
を比較しました。この結果、true
になりますね。オプショナルとオプショナルでない値を比較できるのは非常に便利です。これがないと毎回オプショナルを解除して比較する必要があり、非常に煩雑になります。
例えば、C++だとポインタを使用する際に、nullptr
の場合はアクセスできないため、ユーザーが両方を確認して比較する必要があります。C++ ではそのように面倒くさいことが多いですね。この Swift の仕様があることでコードが書きやすくなり、読みやすくなります。
また、オプショナルな変数や定数を使うと、必ずオプショナルの形になってしまいます。例えば、value1
が Int
型で value2
が Int?
型のときに、この変数を使って比較すると、必ず片側がサブタイプとみなされ、オプショナル同士の比較が行われます。これがあることで非常に便利です。
もちろん、安全性も確保されています。片方が String
の場合、Int
型は String
オプショナルのサブタイプにはならないので、その場合はエラーが発生します。ニルリテラルの場合、ニルリテラルはオプショナルにしかなり得ず、ニルでしかあり得ません。そのため、片側がオプショナル型だったときには全く問題なく比較ができます。
例えば、if value1 as Int? == nil
と書いても警告が出ません。これはオプショナル型に変換したことによって、違う演算子が呼び出されるためです。ニルリテラルを想定した演算子とオプショナル型を想定した演算子の違いによるものです。
このようにして、Swift のオプショナル型は非常に便利であり、かつ安全に使用できる仕組みが整っています。 なので、回避したいときはこういう書き方として一応ありですね。回避したいときがどういう場面なのか、警告でわざわざ出してくれているところで、このコードが正しいんだよという場面が具体的に思い浮かばないですけど。
ということで、ここが成り立つのは、Int型はIntオプショナルのサブタイプなので、警告をスルーしたいときに適しています。ちなみに、ボイドを返すところでこういうリターン文の際にセミコロンを使って回避できるかもしれないですね。その場面ではよくわからないままですが、セミコロンを使うことで警告を回避できます。
例えば、何かのときに警告が出ていない場面だとして、仮にここで警告が出るような状況だったときに丸括弧を使うだけで警告を回避できる場面です。これは、丸括弧を書くことでプログラマが意図的に書いたということを示せる必要があるという場合があります。なので、もしかするとこういうパターンかもしれないです。
よって、もう一つあり得る可能性としてこういうものを考えてみましたが、今回はその構文が用意されていません。他の警告にはあるかもしれませんが、そういったところも一応踏まえながら警告と対処していく感じになります。これは完全に自分としてどれが一番納得のいく話の流れかという問題に帰着します。そして、完全に納得いかない人もいれば、言っていることがわからないという人もいると思いますが、それはそれでOKだと思います。それを踏まえ、自分はなんとなく今回の話を進めていく中で納得できた気がします。
つまり、オプションじゃない型と見るというのは比較できないという前提でいると、話がうまく進む気がします。まだわかりませんが、これからの学びによって更新されることもあるでしょう。基本的に、この構文は間違いと捉えて良さそうだというのが今の自分の感想です。ただし、これを実現するために標準ライブラリがいろいろ頑張っている部分も気になるところです。
ですので、「Optional Nil Comparison Type」を使って演算子を作成し、比較ができるよう構文エラーにならないようにしています。実際、この演算子を使って比較を行うのは構文エラーにならないように工夫されています。このことを念頭に置くと、この意図も重要ですが、ニルリテラルの際の演算子もわざわざ定義されているのです。
オプショナル型ですが、ニルリテラルから変換できない場合やその可能性を見落としているかもしれません。また、この演算子は必ずしもニルを取らないオプショナルじゃないインスタンスを想定するわけではなく、オプショナル型になっています。オプショナルの時だったとしても、エラーにならないようにするための演算子が用意されているので、この点について理解が深まりました。
じゃあ、今日はこんな感じのお話で終わりにします。次回は、イフ文の続きについて話します。イフ文ではいろんな形がオプショナルで飛びますので、今日話したイフ文以外にもいろいろなパターンを紹介しつつ、それから派生してオプショナルの話をしていこうと思います。
それでは、今日の勉強会はこれで終わりにします。お疲れ様でした。ありがとうございました。