Swift の struct の stored property は var にしよう

  • 61
    Like
  • 0
    Comment

記事について

この記事は potatotips#39 という勉強会で発表した内容を再掲したものです。スライドの画像と、喋った言葉を載せました。

以下スライド


1.jpeg

それではこれから、Swift の struct の stored property は var にしよう、というタイトルで発表します。


2.jpeg

まずは自己紹介します。名前はおもちめたるといいます。いろんなプログラミング言語を書いてます。キータで時々記事を書いています。今日は Swift の発表に来ましたが、 Kotlin の記事が一番バズってるので Android の人のほうが知ってるかもしれません。


3.jpeg

早速このコードを見てください。これはどちらも同じプロパティをもつ Cat の struct です。プロパティが var か let なのかが違います。どちらのコードにすべきでしょうか?このように迷ったことはないでしょうか。


4.jpeg

この発表では、このような場合に、大丈夫だからとにかくvarにしよう、という事を話します。


5.jpeg

僕の経験上、letにするべきではないか、考える人は、immutable な設計を意識している事が多いように思います。定数をなるべく使おうね、という考えです。その考えには僕も賛成です。


6.jpeg

ですが、その発想のもとでなお、var にしたら良いと考えています。なぜなら実は、 struct なら var でも immutable と 「みなせる」からです。これについて話します。


7.jpeg

まず Immutable クラスについて確認します。簡単に言うとプロパティが全部 let のやつです。あと、だいたい変更作業をするためにビルダーがついてきます。


8.jpeg

これはそのような immutable class の例です。 CCat がそれで、全部 let になっています。 MutableCCat がビルダーで、コンストラクタで Cat を受け取って、var を介して編集して、cat プロパティで完成した新しいインスタンスが取り出せます。


9.jpeg

さて、実は、var 持ちの struct は、このような immutable class と builder の 2つと同じように働きます。一人二役のような感じです。


10.jpeg

それではこれから、 struct を immutable に感じる考え方を説明します。このコードをみてください。structBreeder は struct の Cat を持つクラスです。この cat の name を変更しています。そして Swift ではこのコードが・・・


11.jpeg

この3行のコードと等価です。一度 var で変数に受けて、変更してから、また戻してやる、というコードです。そして、このコードをさっきの immutable class ではどのようになるか示します。


12.jpeg

このように、 breeder の cat を ビルダーで受けてから、変更して、新しいインスタンスを作って差し替える、という流れです。この3つのコードを眺めていると、だんだん最初のコードが、 name を変更するのではなく、 name を変更した新しい cat を作ってそれと差し替えているように見えてきます。


13.jpeg

いやいやおかしいじゃないか、同じインスタンスを変更してるんだから、新しいインスタンスと入れ替える immutable とは全然違うじゃないか。と思うかもしれません。


14.jpeg

でもここが重要です。同じインスタンスを変更したのか、新しいインスタンスに差し替えたのかは、そもそもそのインスタンスが共有されていない限り区別できないのです。 シュレーディンガーの猫みたいな感じです。


15.jpeg

ここで共有という概念が重要になってきます。


16.jpeg

そもそもなぜ mutable は嫌だ、という事になるか例を示します。このコードは class の cat をもつ classBreeder1 が、その cat を breeder2 にコピーして、そのあと name を変更する、というものです。しかし、 cat が共有されているので、意図せず breeder1 の cat も変更されてしまうのが嫌だね、という事です。つまり、他の共有者から意図せず変更をされてしまうことがあるから嫌だという事です。


17.jpeg

さて、こちらは struct で同じことをやった場合ですが、最後の print によって、 breeder1 の cat の name が保持されている事がわかります。これはなぜかというと、この、 breeder1 から breeder2 でコピーするところで、実体コピーが発生して、新しいオブジェクトが出現するからです。つまり struct では、共有しようとしてもその瞬間に新しいオブジェクトができるので、共有されることがなく、常に専有されるわけです。


18.jpeg

巻き戻しながらまとめていきます。structは共有することができません。だから、実体の変更と新しい実体への差し替えが等価になります。そうすると、 var でも immutable class と等価という事になります。なので var でも安全だから var にしておこう、という事です。


19.jpeg

発表はこれで終わりなんですが、このテーマに対して本当は4倍ぐらい喋りたいことがあるのですが入り切りませんでした。最近は Discord というチャットで Swift の話をしているので、興味がある人はこういうところで話しましょう。以上で発表を終わります。