rustcを通らないコードは間違っている

Rustを書いていると、慣れるまではrustcに頻繁に怒られる。慣れても結構怒られる。とにかくrustcに怒られる。きっと貴方はこう思うだろう、「Rustはなんて煩い言語なんだ!俺の愛する****(好きな言語の名前を入れてください)ならばこんなことはないのに!」

このように「rustcが煩くて俺のコードが通らない」場合、とりあえず自分のコードが間違っていると疑って問題はない。え、「俺のコードは正しい」? 違う違う、コードのロジックの話じゃない。「そのコードがRust wayではない」という点で「間違っている」。microなスタイルからmacroな設計まで、ありとあらゆる点でRust的なコードであることを暗黙的ではあるが極めて強く要求する。それがRustというプログラミング言語だ。時たまrustcが間違っていることもあるんだけど、体感値として97%はrustcの方が正しいと言っていい。

なぜこのように「不自由な」言語なのか? それはRustは「人間が考えなくても静的に解析できる情報を増やせば、コンパイルの時点でリソースの取り扱いに関するバグを減らせる」という世界観で構築されているからだ。それを実現する為に「言語仕様としてセマンティクスを強く定義し、rustcでそれを機械的に徹底検証する」というモデルを提供している。

こういう世界観で構築されているので、論理的に検証できない・すなわちコード内に自明ではない整合性はRustにおいては、しばしばrustcに禁止させている。パワーバランス的には人間よりもrustcの方が強い。コンパイルの通らないコードはそもそも動かないからだ。セマンティクスと相俟って人間はrustcが解釈できるようなコードを書く必要がある。GoとかTypeScriptよりは間違いなく圧が強い。見返りに、人間はRustの世界観を受け取る。Rustはコンパイルさえ通れば、(後述する例外はあるが)書かれたコードの通りに動く。書いたコードがバグっていればバグ通りに動く。 Noneunwrap()すればそこでクラッシュする。 { ... }でスコープを縮めてやればlockは縮めた分だけすぐに解放される。それがRustである。「〜っぽいコードを書こう」なんて、ゆるふわは無い。勧められるままに書かねば何も動かないか無駄に歪なコードが残るだけだ。

このように世界観の主張が強いので他言語のコードを安直に置き換えても上手く行くとは限らない。moveで済む場合はborrowせずmoveせよ、goroutineみたいにスレッドをたくさん建てずアクター的にせよ(これはownership制約と同時にカーネルスレッド使ってるのが理由だったりもするけど)、下手に高次の言語からportするよりも、スレッドも何も使ってないCやJavaScriptからportの方が簡単かもね。とにかくRustの世界に合わせてコードと設計を変えなければならない。さすればrustcはコンパイルを通してくれる。

この思想故、Rustは言語機能こそ多いが局所局所で取れるmicroなパターンは極端に多くない。syntax的には組み合わせられても、micro/macroでのownershipやlifetimeによって組めないパターンが幾つも現れる。

もちろんそんな理想論ばかりが通ることはない。C/C++の資産との接続、明らかに安全かつ性能のためのdirty hack。そんな時はコードをunsafeに括り出せばよい。ただし全てunsafeにしてはいけない。unsafe由来のコードはRustの世界を容易に侵食し、コンパイルが通ったコードがいきなりセグフォで落ちるようになりかねない。なので、必要最低限の分だけ括り出さねばrustcの加護は得られない。このため、FFIを用いるbinding crateは***-sysという薄いFFIするだけのwrapperと、それの上にRustの殻をかぶせたAPIの二段構成を取ることになる。 いくらrustcが全てを視てくれると言っても、そのために全ての変数の型やlifetimeを明記するのは怠い。故に推論による省略が出来る。

rustcに無制限の自由を差し出す代わりに、偉大なるrustcの監視の目を用いてコードの穴は塞がれる。代償とした自由は大きくも見えるが、現実的に取り得るコードのパターンに於いては概ねプログラマ側に自由があるので問題はない。発想が少々oxydizeされるだけだ。プログラマはロジックを書くのに専念すればよい。IDEに一定範囲の自由を売る代わりに、IDEによってコードやリポジトリに対する人間の認知力を拡張するのと発想は同じ。ScalaとかKotlinとかJavaとかC#とか、あの辺りの作法と大差はない。問題のレイヤーを低次に下げた結果、機械に売り渡す自由は増えたが、多くはレイヤー固有の事情なので問題にはならない。

(とはいえ今のRustはIDE支援が半端で色々片手落ちになってるので、Rust Projectの2017年のRoadmapに「IDEを頑張る」って入ってるんだけど)

言語に合わせて思考と設計を変える。今までの固定観念を捨ててRustに合わせて再構築し直す。これがRustでコードを書くということだ。