わふうの人が書いてます。

iOSアプリケーション開発、BLEのファームウェアとハードウェア試作のフリーランスエンジニア。

LeetCodeでrustを始めました

LeetCodeで、書いて動かして身につける

Rustというプログラミング言語 https://www.rust-lang.org を身につけたくなりました。

ただ言語を覚えても使えないのですが、LeetCode https://leetcode.com というサイトで練習問題を解いています。かなり、いいです。

なぜRustかといえば、もう、組み込み向けのファームウェアで、memset(*some_ptr, sizeof(some))して構造体を初期化しているつもりが、some_ptrがsome1という1バイトだけサイズが小さい構造体で、隣の1バイトに0を書き込んでしまって、隣にある構造体がたまたま先頭が通常は0である時だけフラグ1が立つフィールドで、滅多と異常な振る舞いはしないけど、たまに異常な振る舞いをする、実行状態依存のバグ調査なんて、二度としたくないからです。とはいえ、C++は私には難しすぎます。

Rustのドキュメントは https://doc.rust-lang.org にあります。日本語訳を出されているサイトが https://doc.rust-jp.rs にあります。ここには、プログラミング言語 Rust, 2nd Edition(最新版)/ The Rust Programming Language, Second Edition の日本語版が https://doc.rust-jp.rs/book/second-edition/ にあります。

他の言語から推移する

プログラミング言語を習得するには、最初にしっかりと基本的な説明を読み、その言語で素直に書けて他人が読みやすい表現を身につけていくのがよいと思います。素直にかけて読みやすい表現は、小さな1つの完結したプログラムを、数をたくさん書いて動かしてみるのがよいのではないでしょうか。

Rustをいきなり書くのは、無理です。全く訳がわかりません。だから、まずSwiftで動くコードを書いて、それをRustに書き直すことをしています。なぜSwiftかといえば、iOSアプリ開発が本業なので、今時の言語で今覚えている言語がSwiftだったからです。WPFアプリを書いていた20年程前なら、C# だったでしょう。

C言語で書くのは、全くの無意味だなと思います。今時の言語は、イテレータやら辞書やらがあって当たり前ですが、そんな概念がない言語でコードを書くのは、辛いですし、その言語で書いたコードを今時の言語に訳し直すのは、今時の言語の当たり前にある恩恵を全く無視することになり、やる意味がありません。C++という選択肢があるとは思いますが、C++は私には難しすぎます。

なんの工夫もない力づくのコードを、まずSwiftで書きます。処理自体は正しくても、実行時間が長すぎて不合格になったりします。そのコードを、Rustで書くと、十分に短時間で処理が終わり受け付けてもらえるのをみると、Rustを学ぶ理由は速さと自分に納得感が出てきます。

トロイア遺跡の発見で知られるシュリーマンは、ものすごい数の言語を習得していたそうですが、その習得方法の一部に、すでに読んだことのある本の翻訳版を何度も繰り返して読むことがあったそうです。ほんとかどうか知りませんけど。すでに知っている言語で一度書いて、それを翻訳するのは、基本的な部分を身につけるには良さそうです。

XcodeとVS Codeでプレイグラウンド

LeetCodeのサイトは、ハイライト表示などのコードを書く支援機能もあり、コードを快適に書けます。ですが、身につけていない言語でコードを書く場合は、リアルタイムな入力補完や文法エラーの指摘が欲しいところです。

いわゆる、プレイグラウンドがローカルに欲しいところです。

Xcodeは、Macに無償でインストールできます。Xcodeで、メニュー新規作成からプレイグラウンドを作成して、Swiftで書いたコードをその都度評価、その都度実行ができます。

Rustの始め方は、 https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/getting-started.html に書いてあります。

Rustのプレイグランドな環境は、VS Codeを使います。

単体のソースファイルを実行するには、extensionで、Code Runner 0.9.8 をインストールします。Rustの適当なファイル(例えば main.rs のように拡張子をrsにしておけば)を新規作成して、適当にコードを書いて、右クリックで”Run code”をすれば、rustcデコンパイルしてくれます。コンパイラのエラーメッセージでコードが書けるなら、これでいいでしょう。

さらにリアルタイムな入力補完や文法チェックなどが欲しいなら、extensionで Rust (rls) 0.6.1 をインストールします。さらに、rustのCagoプロジェクトを作れば、依存関係も見た入力補完が利くようになります。

Rustの使いどこ?

Rustはべメタルからサーバーまで、幅広く使える言語ですが、私の個人的な要求は、数KBのメモリしかないマイコンで、今時の言語で開発をしたい、ただそれだけです。

今時の言語であれば、そもそも、やらなくてもいいこと、言語に組み込まれたあらゆる工夫や技術開発のおかげで、今ではもう苦労する必要がもうないものを、考古学的に現代で縄文人の生活をするようなことは、もう嫌なのです。苦労は買ってでもしろというなら、店頭に並べておくので、いくらでも買っていって欲しいくらいなのです。

forループで配列を処理するのに、配列の長さを間違えて、書き込んではならぬメモリ領域を暗に変更して、それがずっと後になって何かの処理に影響を及ぼしたり、あるいは及ぼさなかったりするのは、実際に実行されているあまたのデバイスで、稀に発生するが再現できない何かがあると言われるのは、もう嫌なのです。

配列の長さを確実にチェックするのに、{some_type * array_ptr, int count } と、わざわざ自分で配列の長さを記録して配列の処理ループを書くのは、嫌なのです。

今時の言語ならイテレータを使えば済むことを、わざわざforループでベタがきして、潜在的なバグを発生させる綱渡りのようなコードを、大昔に開発された言語で今書いて、地雷を自分で埋めて自分で踏み抜くのが、嫌なのです。

とはいえ、数KBくらいしかメモリがない、OSもないマイコン向けのバイナリを吐き出せる言語系というと、C言語かC++言語になっています。これらは直接機械語を吐き出します。今時の言語は、例えばJavaにしてもSwiftにしても、配列アクセス時の範囲チェックやメモリ管理などが必要ですから、ランタイムがコードを実行しています。ランタイムは、小さなメモリのマイコンに移植するのは、例えばtinyCLRなど実装例がありますが、1桁KBには無理です。

Rustを使ってみると、処理経過がスタックにズラーっと並び、しかも無駄なコピーも発生させずに、処理が終われば長くのびたスタックが元の長さに戻り、その先頭には処理結果がちょこんと乗っている、そんなメモリの時間軸の流れと、処理の流れがイメージできます。

Rustで書いたコードはデータの局在性が極めて高くなりそうだし、そうなるようなコードを書くのがRustでの素直に読めるコードになるのでしょう。最近のプロセッサの処理速度は、演算回路それ自体よりもむしろメモリ幅で制約される場面も多いでしょう。

ハイエンドでも、いい言語かもしれません。データの局在性で。プログラム自体がバイナリが小さい=キャッシュに収まる、スタック的に処理が進む=以前にアクセスした領域だから高い確率でキャッシュに残っている、処理経過がスタックが伸びる感じで進む=データ局在性が極めて高くキャッシュヒットが高くなる感じのコードを書くことになるのかもしれません。もちろんRustにでもRcを使えば参照メモリが使えて、JavaやSwiftと同じようなメモリ領域の使い方ができますが、そういうコードを書く言語ではない、それだけのことなのでしょう。最近のプロセッサはL3キャッシュが2桁MBだったりしますから、そのキャッシュに丸っと処理系が入って出てこないなら、もうプロセッサがFPGAと見分けできなくなりそうですね。