時間がないので遅ればせながら

ハッピークリスマス!

ウキウキする季節が来た!
サンタさんと手を繋いで!

一緒に幸せゲットだよ!

あんたクリスマス最終決戦の真っ只中だろ!

まほプリの最終決戦はまだだろうか。

実装の継承の禁止: rustのDeref coercionsはdeferd classするためのものではない

Deref coercionsは型シノニムになっているowned valueからborrowed valueへと型強制するためのものです。
ですから、関数のシグネチャがfn deref(&self) -> &Self::Targetの&Self::Targetのように参照となっています。

そのため、これをdeferd classを実現するために使ってはいけません。それはrustにおけるアンチパターンのひとつです。

操作が参照外しであるにもかかわらず、戻り値の型が参照になっている部分を見て気づいて下さい。そこがミソです。
そして、強い型付けを行うrustにおいてauto derefは唯一、暗黙の型変換を行う部分です。これはjavaのboxing conversionsとauto boxing and unboxingの関係と同じです。

javaのauto boxing and unboxingはindyの文脈でかなりの頻度で利用することになりますが、rustのDeref coercions(とauto derefの組み合わせ)はborrowed valueを受け取る関数に型シノニムになっているだけのowned valueを暗黙の型変換を利用して渡すなどすると便利なときに使います。

──まずはじめに、AsRef、BorrowとDerefの違いを説明します


AsRef、BorrowとDerefとDeref coercionsのコンビの3種は使い分けが曖昧ですが、AsRefはAsRefがreference-to-reference conversionであることを利用してライブラリ側がborrowed valueを受け取るがAsRefへ明示的に変換可能なowned valueをも受け取れることを明示するために利用します。
そのほかには単純にcollection likeなオブジェクトの自分自身の参照を返すときなどにもよく使います。このときは自分が自分の参照を使います。他人には貸しません。

Borrowはborrowed valueを他人に貸すときに使うが、元のowned valueをpolymorphicに扱いたいときの量化された値を表す型です。このためgenericsとよく組み合わせます。たとえば、keyとマッピングするcollectionのkeyにStringでも&strでも受け付けたいときなどに使います。

AsRefとBorrowの違いはそれぞれ、AsRefが変換操作でBorrowはpolymorphicなowned valueのラッパーをあらわすtraitです。

そして、Derefはそもそもがcollection likeなオブジェクトのカスタムポインタを返すために使うようですが、頻繁に出てくるのはDeref coercionsの方です。
AsRefとDeref coercionsの使い分けは、ライブラリ側がクライアントコードに配慮するか、クライアントコード側がライブラリにあわせて型変換するかの違いです。
クライアントコードがDeref coercionsを利用した場合、auto derefによってDeref coercionsが起こるので便利という算段です。明示的に呼び出しても構いませんが。

しかし、パターンマッチでは型は厳密に一致しなければならないことに気をつけましょう。Stringと&strはマッチしません。

──そして、話を戻します



AsRef、BorrowとDerefの違いを説明したところで話を戻しますが、Derefをdeferd classを実現するために使ってはいけません。アンチパターンです。

そういう変換のためのtraitは実はrustの内部で利用されていてAsInner,IntoInner,FromInnerという非公開のtraitとなっていてDerefとは組み合わせていません。rustの設計思想ではinnerの取り出しは明示的に行います。
rustでライブラリを書いているとinnerを取り出したいことが頻繁にありますが、それでもそういうときにはself.0self.innerとしたりして必ず明示します。

これはrustが実装の継承を禁止する設計思想の言語だからです。しかし、Eiffelほど実装方法の選択肢がない所を見ると実装の継承そのものを強く禁止しているように見受けられます。

実装の継承の禁止の考え方は昔からあってすでに約30年前にEiffelが実現しています。
30年前というとC++の時代ですがそのときから同じOOPでも違うパラダイムを持つ言語があったことが伺え、決してC++のclassが銀の弾丸ではないことを表しています。実装を継承したいのではなく、型を定義したかっただけなのです。

Eiffel以外にもjavaについてJames Goslingがインターフェイスだけにしとけばよかったと言っています。最近の言語はこの傾向が強いのですが、この設計思想を理解していないとrustのDeref coercionsの使い方を間違えます。



  • classの問題点はなにも多重継承だけではないのです。
  • deferd classとは抽象クラスやインターフェイスのことです。実装を遅延するからdeferd classです。
  • ちなみにEiffelは暗黙の実装の継承を禁止し実装方法を数ある選択肢の中から明示しなければいけません。これによってメソッド検索のパスが曖昧にならないので多重継承を許可しています。
  • rustは実装の継承の禁止だけでなく部分型も定義できません。部分型多相があるのはリージョンだけです。さらにリージョン多相かつリージョン推論する言語なのでここらへんも理解する必要があります。実は無駄な型変換をしているときがよくあります。
  • そのくせただの複文にリージョン指定できません。(構文的に許してないのでやるとwhileなどのリージョン指定とかち合ってコンパイラに怒られます)

https://github.com/rust-unofficial/patterns/blob/master/anti_patterns/deref.md
https://github.com/rust-unofficial/patterns/blob/master/idioms/deref.md

rustupでdocumentをインストールするときの注意

現在、rustupでrustup component listすると以下のように出てくると思います。


rust-docs-<target-triple>
rust-src

そこで、rustup component add rust-docs-<target-triple>するとエラーと言われます。
rust-docs-<target-triple>ではなくrust-docsが正しいので注意しましょう。rust-srcのようにtarget-tripleがつかないのが正しいコンポーネント名です。

documentバグってました。

(4ecc85beb 2016-12-28)で直っている

逃げ恥の聖地

──といってプリキュアの激戦地へと赴いたモノノフは正直に手を挙げなさい!
正直者にはイオンモール浦和美園に行く権利を授けましょう。

資格の件、DBはやってりゃそのうち取れるもんじゃないかね。

話は変わるが先週と今週にかけてロシアのリファラスパムを全く同じアクセス数で受けている様子。
そのせいでbloggerの統計がロシアロシアなことになってしまっている。

正直最近こっちに書くことがない。あるにはあるんだけど。