Skip to content

Instantly share code, notes, and snippets.

@asa1984
Last active March 27, 2025 14:45
Show Gist options
  • Save asa1984/8f540850a59f902009b726a6ae23daa5 to your computer and use it in GitHub Desktop.
Save asa1984/8f540850a59f902009b726a6ae23daa5 to your computer and use it in GitHub Desktop.
Rust最短攻略経路 / asa1984 Advent Calendar 2024 2日目

Rust最短攻略経路

「asa1984 Advent Calendar 2024」の2日目。

本稿の目的は、「Rustは難しい」という言説を打破し、The Rust Programming Language を前にして立ちすくむ人々の目を †本質情報† に向けさせることである。よって、本稿ではRust言語のより効率的な学習経路を提示する。

ところで、読者諸君からは「なんかエラそうなこと言ってるけど、筆者はどれくらいRustを使えるの?」という疑問が湧き上がっているかもしれない。そんな読者にはこれを見てほしい。





ㅤㅤカニ

✌👁👁✌ㅤ=͟͟͞͞‪
ㅤλㅤㅤλㅤㅤ=͟͟͞͞‪





対象読者

真にRustを攻略しようとするとスーパーエンジニアにならないといけないので、ここでの「攻略」は「Rustを使ってCLIツールやバックエンドサーバーなどのアプリケーションを作れるようになること」を意味することにする。
というか筆者がそのレベルに達しているかいないかくらいのレベルなので、それ以上の話ができない。なので、ガチRustプログラマーを目指している人は参考程度に読んでほしい。

最初に

Rustの難しさの正体は、パラダイムの違いである。

パラダイムとは、物の根本的な見方・捉え方のことだ。要は、言語の根底にあるメンタルモデルが、学習者の既存知識にある言語のものと全く違う。そのため、既存のメンタルモデルと新しいメンタルモデルが衝突し、学習者に大きな混乱を生んでしまう。実際、Rustを学ぶぞとなったとき、既に他の言語の経験がある人よりも、Rustで初めてプログラミングを始める人の方が学習スピードが速い(n=2)。

なので、他の言語1の経験を持っている開発者にとって最も適した学習方法は、一旦記憶を消してからRustを学ぶことになるわけだが、現実にはニューラライザーなど存在しないので、手動で己のメンタルモデルを破壊してやる必要がある。以降は、私の経験を元に、メンタルモデルの更新を行っていく。

高レベル言語・Rust

そもそも、Rustは抽象度の高い言語である。

「言語の抽象度」は、どれだけ計算機の事情を隠蔽できているか、もしくはどれだけ数学的なモデルに落とし込めているか(後述)という指標で語られることが多い。Rustが言語のコンセプトとして掲げているゼロコスト抽象化は、まさしくRustが抽象度の高い言語を指向している証であり、学習を始める前にまず、低レイヤを扱える = 抽象度が低い = 難しいという三段論法的先入観を破棄することが重要である。

関数型言語のエッセンス

次に大事なのが、Rustは関数型言語の影響を強く受けているという点である。構文や低レイヤを扱えるレベルの速度はC++譲り、RustをRustたらしめている数々の言語機能はOCaml譲りだ。

OCamlは関数型言語の1つであり、ML (Meta Language) という言語の方言的なものらしい(Wikipedia調べ)。割とカジュアルに副作用を扱えるのが特徴で、Haskellのようなモナドは存在せず、いわゆる「純粋関数型言語」ではない。我ら独学の民にはなかなか縁のない言語だが、ちゃんとした情報系の大学ではOCamlを対象言語として講義し、コンパイラを作ったりするので、世のCSの学位を持つスーパーエンジニアは大体この言語を知っている(はず)。

筆者は、Rustを難しいと思う人が多い原因は、所有権ライフタイムといったRustの独自概念よりも、Rustの関数型言語チックな側面だと考えている。そして、これは難しいわけではなく、初めて出会う故に困惑しているだけであり、適切な学び方さえすればすぐに突破できる。

型システムの理解

これは完全に筆者の主観だが、Rustを学ぶ人のほとんどは、同時に関数型言語由来の表現力の高い型システムに触れるのも初めてである(筆者も初めてだった)。

Rustは、Hindly-Milner型システムという強力な型推論を行える型システム(をちょっと拡張したもの)を搭載している。HM型システムはHaskellやOCamlにも搭載されており、これらの言語に強力な抽象化能力を提供している。

ここで型システムを全く理解していなかった頃の私は、「型システムが一体全体どう言語の機能を豊かにするんだ?」という疑問でいっぱいだった。というのも、型システムとはC言語のintとかそういうもので、メモリ上にどう値を配置するか決めるものだと思っていたからだ。

このような理解をしている人は、型システム入門の第一章を読んでほしい(PDFを無料で読める)。型システム入門(通称: TAPL)は、既に絶版だが型システムを学ぶならこれ!という聖書である。ただ、タイトルの「入門」はあくまで大学院生や研究者に向けた「型システムを研究するにあたっての入門」という意味であり、オライリーの入門みたいなノリで買うととんでもない挫折を味わう(一敗)。ただし、一章はやや難しいことが書いてあるものの、一般向けの文章なので問題ない。

重要なのは、TAPL一章ではC言語の型システムが寧ろ例外のような扱いを受けていることだ。TAPL、ひいてはプログラミング言語の研究の世界における「型システム」は、(型システムに明確な定義がないという前置きをしつつ、)プログラムを実行する前にある種の振る舞いを起こさないこと(雑に言えばプログラマーのミスでバグが起きないこと)を検証する軽量な形式手法である、とされている。

「形式手法」という言葉が出てきたが、これはプログラムを実行する前に、数学的な証明を行って誤りがないか検証を行う手法である。これは大変コストのかかるもので、基本的に金融とか航空宇宙産業とか誤りがあると取り返しのつかない分野でしか用いられない。型システムは、これをプログラマーが馴染みやすいようにプログラミング言語の構文に取り込みつつ、検証能力を多少犠牲にしつつもコンパイラに内蔵できるレベルで軽量に実行できるようにしたものである。

つまり、型システムはある種の数学的証明のようなことを行っていて、そんな凄そうなものを多くの静的型付け関数型言語は搭載しており、それに強く影響を受けているとるRustも当然それを持っているのだ。そして、この凄いシステムは足枷でもなんでもなく、プログラマーを助けてくれる心強いツールなのだ。

実用編: 直積型と直和型

型システムは高度な安全装置であるだけでなく、高度な抽象化能力を提供する実利的な言語機能である。

Rustのstructenumは、それぞれ直積型、直和型というものに相当する。これらは何で、何がいいのか?という問いに答えると私のキーボードを打つ指が弾け飛びそうなので、適当な技術記事を調べるか、『関数型ドメインモデリング』という本を読むことをおすすめする。この本は、型システムに対する実用的な理解を更新するのに大いに役立つので、Rustでなくとも静的型付け言語を使っている方は是非読んでほしい。

軽く説明だけすると、型は現実世界の「状態」をプログラム上のモデルに落とし込むための優れた道具になる。

借用チェッカー

Rustの難しいポイントとして、所有権が挙げられることがあるが、これは全然難しくない。難しいのはライフタイムの方であり、大抵の人(私のこと)は所有権とライフタイムをごちゃまぜに理解している(両者に深い関係はあるが)。

所有権は、ミュータブル vs イミュータブルという二項対立的なパラダイムに一石を投じる、新しいアプローチである。所有権もとい借用チェッカーは、プログラムから共有可変状態を排除するため、言語に所有権という概念を導入して静的な検査を行う(あれ?"静的な検査"?)。

共有可変状態の意味はググってもらうとして、これがあると問題が起きやすいので、開発者が意識してできるだけイミュータブルにしよう!というのが、現代において広く共有されているベストプラクティスである。ほとんどの関数型言語は、そもそも変数が全てイミュータブルになので、(パフォーマンスや特定ケースでの書き味を犠牲にしつつ、)この問題に対処できている。

一方で、Rustはデフォルトイミュータブルでありつつ、ミュータブルな変数も宣言でき、なおかつミュータブルな変数が共有可変状態にないことを機械的に保証できるのである。これを実現するのが借用チェッカーだ。

借用チェッカーのベースになっているのは、分離論理という形式手法の一種(!)である。つまり、借用チェッカーは型システムの親戚みたいなもの、というかほぼ同じ存在だ!

詳しくは以下の記事を読んでほしい(とてもわかりやすい記事!)

Visions of the future: formal verification in Rust

実際に学ぶ!

The Rust Programming Language全部読め!

この本だけで以後Rustを学ぶ旅に出るための十分な滑走路になる(ただし、動的ディスパッチの話をしている部分と20章だけ和訳が遅れているので、そこだけ英語版を読むとよい)。というか、CLIツールくらいならLLMを駆使して頑張れば作れるレベルになる。

しかし、バックエンドサーバーを作りたい開発者は、非同期ランタイムという、Rustの真に難しいものその2に挑まなければならない。

20章 -> tokioチュートリアル

The Rust Programming Language 20章でRustのstdだけでサーバーを実装したら、次はtokioチュートリアルに飛ぶといい。ただし、前提知識が必要である。

まず、OSのスレッドのCPUスケジューリングとコンテキストスイッチの知識はあったほうがいい。これらの知識は、tokioで登場する概念・グリーンスレッドの理解に非常に役立つ。OSの話は学校の授業で学ぶのが一番よい方法だと思うが、それができない場合は、放送大学の無料で見れる講義を見るなり、YouTubeに転がっている異常な熱量のコンテンツを見るなり、教科書を買うなりするとよい。大学の講義用PDFくらいなら無限に転がっていると思う。

20章 -> tokioチュートリアルという流れを重視するのは、これが綺麗に歴史の流れに対応するからである。20章の実装は、C10K問題を引き起こす伝統的な実装である。そして、C10K問題を解決するために、Node.jsのイベントループやGoのgoroutine、そしてRustのtokioといったアプローチが生まれてきたという歴史的背景がある。これについては、以下の記事が大変参考になるので是非参照されたし。

Webサーバーアーキテクチャ進化論2023

JSのasync/awaitは忘れろ

JSのasync/awaitとRustのasync/awaitは全く完全に異なるものなので、Rustの非同期ランタイムを学ぶなら必ずJSの記憶を消すように!

別解: Effect Systemから学ぶ

Rustのasync/awaitは、Effect Systemというプログラミングパラダイムと似た部分があるらしく、筆者も素人ながらそれっぽさを感じたので、ここから攻めるのもありなのかもしれない?(有識者求む)

結論

Rustムズカシクナイヨ!

✌👁👁✌ㅤ=͟͟͞͞‪
ㅤλㅤㅤλㅤㅤ=͟͟͞͞‪

Footnotes

  1. これは一般的な手続き型言語のことを指している。HaskellやOCamlは例外。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment