Rustの学習曲線をなだらかにするガイド
(corrode.dev)- Rustはまったく新しい考え方を求める言語で、心構えが学習速度に大きく影響する
- コンパイラと仲良くなることが鍵であり、エラーメッセージを単に直すのではなく、理由を理解する姿勢が重要
- 初期段階では
clone(),unwrapなどを積極的に使い、小さく始めて段階的にリファクタリングするのがよい - コードを手でたくさん書き、ミスや試行錯誤を通じて直感と筋肉記憶を積み上げるべき
- Rustは型システム中心の開発という哲学を持っており、ドキュメントを丁寧に読み、型でモデリングする練習が必要
Flattening Rust's Learning Curve
Rustは学ぶのが難しい言語として知られているが、この記事ではRustをより効果的に学ぶための姿勢とアプローチについて、著者の経験をもとに具体的な助言を示している。
Let Your Guard Down
- Rustは既存の言語とは異なるメンタルモデルを要求する
- 初心者が上級者より早く身につける場合もある → 姿勢と開かれた態度が鍵
- 借用チェッカー(borrow checker) を敵ではなく共同執筆者と考え、エラーメッセージを理解しようと努めるべき
- コンパイラが lifetime パラメータを要求する理由を深く理解しようとする姿勢が重要
- コードが見苦しく複雑であるほど、設計が間違っている可能性があるため、より良い方法を探すきっかけにすべき
- Rustの冗長さ(verbosity) は大規模アプリに役立ち、リファクタリングに有利
clippylint は最初からすべて有効にして活用すべき
Baby Steps
- 最初のうちは
String,clone(),unwrapを惜しまず使い、後でリファクタリングしても構わない - 複雑なメソッドチェーンより、まずは単純な
if,match文から始める - 非同期(async) は最初の1週間は避けるのがよい
- 小さなコード片を Rust Playground で試しながら学ぶ
- 概念1つにつき
main.rsファイル1つで練習し、ほとんどのコードは捨てるつもりで書く
Be Accurate
- Rustでは正確さが生存条件
- タイポや小さなミスがすぐにコンパイルエラーにつながるため、注意が必要
&,mutを自動的に付ける習慣を身につけると役立つTsodingのような開発者の配信動画は良い参考資料になる
Don’t Cheat
- LLMやコード自動補完に依存すると学習が遅くなる
- 自分の手でタイピングし、理解できなければドキュメントを調べる練習が必要
- オートパイロット状態(auto-pilot) でコーディングしないこと
- ミスを受け入れ、それを通じてコンパイラの動作を理解すること
- コードを実行する前にコンパイルできるかを予測する練習も勧められる
- 他人のコードを読んで分析する習慣も重要
- 学習中は外部 Crate の使用を控え、
serde,anyhowくらいを例外として許容する
Build Good Intuitions
lifetime,ownershipの概念は視覚的に理解すると役立つexcalidrawのようなツールでデータフローやアーキテクチャを自分で描く習慣を勧める- 多くの優れたエンジニアや数学者も可視化ツールをうまく活用している
Build On Top Of What You Already Know
- Rustは馴染みのある概念でも動作の仕方が異なる(例:
mut, 値のムーブ など) - しかし既存言語との比較による学習は有用
例:
- Trait → Interface に似ているが異なる
- Struct → 継承のないクラスのようなもの
- Closure → ラムダに近い
- Module → 名前空間
- Borrow → 単一所有者ポインタ
- Option → Maybe monad
- Enum → Algebraic data type
- Rosetta Code は複数言語間のコード比較学習に役立つ
- 馴染みのある言語のコードをRustに移植しながら学ぶのも効果的
- リスト内包表記やループなどの言語ごとのイディオムをRustではどう表現するか考える練習もよい
Don’t Guess
- Rustは推測が通用しない言語
"hello".to_string()のようなコードをなぜ書く必要があるのか、理由を考えるべき- エラーメッセージは非常に有益なので、その中に隠れたヒントを決して無視しないこと
- 特に borrow-checker 関連のエラーはデータフローを手で追跡しながら分析すべき
Lean on Type-Driven Development
- Rustは型システム中心の言語
- 関数シグネチャや型定義などから多くの情報を得られる
- 標準ライブラリのドキュメントやソースを頻繁に読むこと
- 先に型を設計し、それに合うコードを書くことで、より正確で再利用可能な構造を実現できる
- 不変条件(invariant)を型で表現すれば、そもそも誤ったコードがコンパイルされなくなる
Invest Time In Finding Good Learning Resources
- Rustの学習資料はまだ多くないが、序盤で自分に合ったリソースを見つけることは時間の節約に重要
Rustlingsのような学習ツールは相性が分かれるAdvent of Code,Project Eulerのような問題解決中心の教材のほうが適している場合もある- YouTube動画は情報源というよりエンターテインメントとして使う
- 本を買ってオフラインで読み、直接コードを書くやり方が最も効果的
- 可能なら専門家の教育やコーチングを受けることも、長期的には大きな時間短縮になりうる
Find A Coding Buddy
- 実力のある仲間のコードを影のようについて観察するのもよい
- Rustフォーラムや Mastodon などでコードレビューを依頼したり、自分でレビューしたりしながら実力を高められる
Explain Rust Code To Non-Rust Developers
- Rustを知らない人に説明してみることも学習に効果的
- オープンソースプロジェクトの保守されていないコードに貢献するのも勧められる
- Rustの用語を自分の業務ドメインの言葉に対応づけた用語集を作るとよい
Believe In The Long-Term Benefit
- Rustは短期的な生産性より長期的な品質のための言語
- 一朝一夕で上達するのは難しいが、1か月集中すれば多くを得られる
- Rustは「Day 2言語」であり、初日は大変でも継続して使えば価値が大きくなる
- 成功するには単に履歴書のためではなく、プログラミングそのものが好きであることが必要
4件のコメント
以前、学習目的で昔 C で書いていたコードを Rust に書き直してみたことがあったのですが、ポインタを扱うのが本当につらかった記憶があります…。
RcやRefCellなどの動作が頭の中でうまく整理できなくて…基本ドキュメントとノミコンを順番に一度だけ読んだあと、Rustで一度もつまずいたことがなかったので、そんなに学習曲線が急なのかと思います
うわ、
unwrapとcloneに慣れちゃうと、あとで所有権のせいでめちゃくちゃ苦しむことになりそう…Hacker Newsの意見
まるで『A Discipline of Programming』を読んでいる感じ。Dijkstraの道徳的な説明スタイルが昔はどうして必要だったのかといえば、当時はみんなプログラミングという概念そのものを理解していなかったから。Rustの所有権の説明はたいてい冗長すぎる。核心となる概念の大半はあるにはあるが、例の下に埋もれている。Rustでは各データオブジェクトに所有者は正確に1つだけある。この所有権は常に1人だけが持つ形で移譲できる。複数の所有者が必要なら、本当の所有者は参照カウントセルでなければならない。このセルは複製できる。所有者が消えれば、その所有物も消える。
refを使えばデータオブジェクトへのアクセスを一時的に借りられる。所有と参照は明確に異なる。参照は受け渡しや保存ができるが、そのオブジェクトより長く生きることはできない。そうでなければ「ダングリングポインタ」エラーになる。これらのルールはコンパイル時に借用チェッカーによって厳格に強制される。これがRustの所有権モデルであり、理解すれば細部は結局このルールに帰着する自分だけかもしれないが、こういう概念説明は追いづらい。カプセル化も同じだった。ただ情報を隠すと言うだけで、具体的にどう/なぜそうするのかには踏み込まない。たとえばRustで所有者が正確に誰なのかがわからない。スタックフレームが所有者なのか? LIFO構造なのに、なぜ所有権をcalleeに渡すのか、calleeのスタックが先に消えるのだから危険はないのではと思ってしまう。最適化のためならオブジェクトをより早く片付けられるのか? 所有者がスタックフレームでないなら、それが何に当たるのかもわからない。可変参照をなぜ1回しか渡せないのかも混乱する。シングルスレッド環境なら、どうせ1つの関数が終わる前に別の関数は始まらないのだから、両方が可変参照を受け取っても問題ないように見える。マルチスレッド環境でだけ問題が起きるなら、そのときだけエラーにしてもよいのではと思う。こういう疑問のせいでRustを勉強しようとしても何度も挫折してしまう
これは所有権の説明ではなく、動機づけの説明。いちばん難しくて重要なのは、関数シグネチャでライフタイムが複雑に絡んだ場合の読み方と、そうした関数を呼ぶ際に出るコンパイラエラーを理解して修正する方法
すでにこれらの概念を知っている人にとって正しく完璧に見える要約を作るのは、初学者に説明するよりずっと簡単。こういう説明で、call-by-sharingしか使ってこなかった人がすぐ理解できるだろうか? そうは思えない
Rustを知らない人がこの要約だけを読んでも、Rustについて何もわからないだろう。「この言語にはまるでコンパイラにブラックマジックでも入っているみたいだ」と思うだけ
所有権と借用という概念自体は簡単に理解できる。Rustが本当に学びにくい理由は、関数シグネチャと実際の使用コードが、参照はオブジェクトより長生きしないことを互いに証明しなければならないから。ちなみに、参照されたオブジェクトを型の中に保存しないほうが証明が複雑になりにくいので、よりよい選択
60年代の人たちがアセンブリレベルでシステム/アプリケーション状態をどう扱っていたのかについての文章を長いこと探していた。SutherlandのSketchpad論文にはデータ構造関連の詳しい説明が多いと聞いたが、まだ2〜3章しか読んでいない
この説明は自分には意味のあるものとして響かない。所有権と借用を明確に定義していない。どちらも金融資産の管理にたとえたメタファーに基づく言葉のように見える。Rustには詳しくないが、こうした言葉選び自体が概念の理解を難しくしている気がする。メタファーはしばしば諸刃の剣。もっと直接的なメモリ関連の用語で説明したほうが助けになると思う
モデルの説明で重要な排他的/共有(または可変/不変)借用の違いが完全に抜け落ちている。Rustはこうした借用をどう許可するかについて多くの選択をしており、これは直感的ではない。たとえばno aliasingルールは直感から生まれたのではなく、関数最適化の目的から生じたもの。借用で最も複雑なのは、ライフタイム elision ルールのせいでコンパイラエラーメッセージが本当の原因とは無関係な場所を指すことがある点。この elision ルールも直感的ではなく、簡略化のために導入された選択だった
Brown Universityが改訂したRust Bookの版は、借用チェッカーの説明が本当にうまい
説明が不完全に思える。たとえば借りた側が消えたらどうなるかについての話がない
「本当の所有者は参照カウントセルでなければならない」とあるが、それが何かわかる人にしかわからない説明に見える
自分で借用チェッカーを実装してみて初めて、借用チェッカーが理解できる
第2セクションの2つ目の項目はひどく誇張されている。実際には、Rustがコンパイルしないが安全なコードを書けるケースは無数にある。コンパイラが何を証明できるかの限界を明確にするために、こうした複雑さが生まれている
Rustの所有権モデル、ライフタイム、enum、パターンマッチは、最初に触れたとき本当に重荷だった。最初の挑戦ではあまりに早く圧倒され、2回目の挑戦では本のすべての行を読み切ろうとして忍耐力が尽きた。それでもRustがプログラミングとソフトウェア設計についてより深い洞察を与えてくれる言語だと気づいた。3回目の挑戦でようやく、以前書いた小さなプログラムやスクリプトをRust流に書き直しながら学び始め、その過程でRustらしいエラーハンドリング、型を積極的に使ったデータ表現、パターンマッチなども身につけていった。こうした経験を経て、Rustを学んだことは自分のプログラマー人生で最高の決断の1つだったと確信している。型、構造体、enumを先に定義し、不変データとパターンマッチに基づいて関数を書くやり方は、今では他の言語でも自然に適用している
似たような経験をした。Rustを3回目に学んだとき、ようやくしっくり来はじめて、いくつかのプログラムを効果的に書けるようになった。長いプログラミング経験があっても反復学習が必要なことはある。以前、JVMの依存性注入フレームワークであるDaggerを理解するときも、きっちり3回の学習の試みが必要だった。もしかすると、複雑なものを学ぶとき自分に共通して現れるパターンなのかもしれない
C++開発者がRustに初めて触れるときによく見られる現象で、C++の発想でやると「借用チェッカー」と戦い続けることになる。Rustの慣習をきちんと身につけると、その慣習をC++にも持ち帰ってより堅牢なコードが書けるようになる。C++には借用チェッカーがなくても
「コンパイル前にコード全体を丁寧に読み、タイプミスを直せばもっとよい時間を過ごせる」という助言は違和感がある。Rustコンパイラは非常に親切なエラーメッセージで有名なのに、わざわざ自分でタイプミス探しのためにコードを見つめている理由があるのかと思う。コンピュータに自分のタイプミスを見つけてほしい
cargo fixはいくつかの問題を自動修正してくれるが、すべてを解決してくれるわけではない「抵抗するな」「学ぶには傲慢さを捨てろ」「降伏宣言が必要」「抵抗は無駄、早く受け入れないと苦労が長引くだけ」「知っていることを忘れろ」といった助言があって、これを見るとオーウェルのテレスクリーンOSはRustで書かれていたのだろうと思えてくる
Rustは初心者にとってかなり高いハードルがある。他の言語と大きく違うし、それは意図的なものだが、そのぶん参入障壁でもある。構文は複雑で非常に簡潔で、まるで肘でキーボードを叩いたように見える。文字1つで意味が完全に変わることがあり、ネストも深い。多くの機能は理論的背景がないと理解しにくく、そのため複雑さはさらに増す。代表例が型システムと借用メカニズム。一般的なPythonやJavaScriptのユーザーにはほとんど異星語のようなもの。実際、修士レベルのCS背景を持たないプログラマーが大半の時代なので、Rustは向いていないように思う。しかもマクロがさらに複雑さを増している。定義内容を知らないとコードの意味を理解しづらい。最近はLLMがこうした障壁を下げてくれるのではと期待している。まだRustを学ぶ必要までは感じていないが、LLMのおかげでいつかもう一度挑戦してみる気はある。それほどRustは独特に学びづらい言語
どんな状況でもRustよりよい選択肢がありそうに思える。それでも心は開いている。いつかRustが十分に一般的になれば、そのとき意味が出てくるかもしれない
人々が苦労してまで学ぶべきだと説得する文章まで必要な言語なら、言語設計そのものに問題があるのではないかと疑ってしまう(Rustは学んでいないので、あまり深刻に受け取らないでほしいという意味)
あなたのコメントは「難しいものには価値がない」としか読めない。何にでも長所と短所があるのに、短所があるというだけで試す価値すらないという意味なのか疑問だ。ハープ演奏も難しいからといって、誰かが情熱的に文章を書いたら、それでハープ演奏はひどい趣味になるのか?
シニア開発者になると、そのときになって初めてRustが重視している教訓を横目で見たことはあっても、きちんと実感していないことが多い。多くの人は「もうガベージコレクションのある言語を使っているのに、Rustが何を教えてくれるんだ」と考える。実際には可変性と共有参照が絡むとひどく混乱し、そのため不変オブジェクトを多く導入するようになる。不変オブジェクトを持つと、今度はそのオブジェクトを都合よく変形する方法をまた考えることになり、むしろ可変オブジェクトより使い勝手が悪くなることもある。「このオブジェクトには変更可能な時点と不変な時点が別々にある」を表現しようとすると、結局は借用チェッカーの必要性に行き着く。借用チェッカーが導入されると、「では、なぜいまだにガベージコレクションが必要なのか?」という疑問が残る。単にオブジェクトのライフタイムを明確に理解するのが面倒だからガベージコレクションを使うことになる。Rustはこうした根本的な問いを直接体験させてくれる
Rustの設計上の意思決定は理解しづらいことが多い。Mojoにも借用チェッカーはあるが、Rustよりはるかに学びやすい理由はいくつかの選択にある。1つ目は値セマンティクス。Rustでは初心者のうちは常に
clone()を使えと言われがちだが、ふつう静的言語(C、C++、Goなど)では普段から値セマンティクスが基本。2つ目に、Mojoのライフタイムはスコープに応じて値が使えるかどうかではなく、削除のタイミングを決めるもの。参照が残っていればライフタイムは延長され、使用が終わればすぐ削除される。だからMojoでは「値が長く生きられない」といったエラーを見る必要がない。この2つの設計選択だけでも負担はかなり減る初心者にとってはどの言語も学ぶのが難しいので、特にRustだけが特別というわけではない。プログラミングにはもともと学習曲線がある
こういう文章があるということは、言語より著者について多くを語っていると思う。著者批判ではなく、こういう形で情熱を共有するのは素敵だと思う
この記事はRustがどんな問題を解決するのかより、学習曲線の話ばかりしているように見える。両方をバランスよく説明してこそ、本当に挑戦する価値があるか結論を出せる
Rustについての設計論争はいくらでもできるが、こういう文章が必要だという事実だけでRustという言語自体を評価するのは難しい。むしろPythonのほうがこういう文章をもっと必要としている言語だと思う。非エンジニアリング背景のプログラマーが増えるにつれ、Pythonは逆説的に誰でも使えるが、Rustは誰でも使えるわけではない言語になっている。人によってはRustはCやZigのような言語と比べてずっと学びやすい言語でもある。自分はPythonも好きだが、根本的には恐ろしい言語だと認識している。LLMの時代でも、人々は最適化されたPythonを書くべきだということをあまり理解していない。AIの友人たちも、指示しなければ非効率なPythonばかり生成し続けるだろう
「それは言語設計の問題では?」という問いには、「なぜ?」と問い返したくなる
Rustを学んだ立場から言えば、主にPythonしか使わないとしても、Rustに言語的欠点があると感じたことはない。むしろ非常に厳格な言語なので、Rustらしくないやり方をしようとすると苦労が増えるだけ。Rustのスタイルに従えば、複雑になればなるほどむしろ大きな助けになる。他の言語ではエラーが実行時に1つずつ見つかるが、Rustではほとんどがコンパイル時に捕まる。もちろん論理エラーまでは防げないが、強力なテスト統合のおかげで対処できる。欠点もあるが、Rustは一度は学ぶ価値がある。Rustがエラーを減らすために採った方法は、他の言語でも良い開発習慣として活かせる
Rustはあまりに複雑なので、LLMが正しいRustコードを一発で生成するのは難しいのは事実。それでもJavaScriptや他の弱い型付け/動的言語のさまざまな問題よりは、そのほうがずっとましだと思う
自分はRustを学んだし、あなたの話にも共感する。Rustは本当に複雑で、「委員会設計」の言語だ。素晴らしいツーリングはあるが、それでもC++よりは複雑さが少ないというだけで、決して簡単に学べる言語ではない
こういう文章の問題は、本質に届いていないこと。Rustには、そもそも書くこと自体が許されないプログラムがある。十分な理由はあるが、これはほぼすべての人がこれまで使ってきた言語と本質的に違う。明らかにRustでは書けないプログラムがある以上、それを受け入れる必要がある。受け入れられないならRustには向いていない
Rustを学ぶときによく取られないアプローチの1つは、まず言語の一部だけを身につけること。たとえば自分のRust入門書ではライフタイムをまったく教えない。ライフタイムなしの関数だけでも、十分に動くプログラムは書ける。マクロも同様で、簡単ではないにせよ、とにかくまずはサブセットから学ぶべき。そして最初から
copy()やclone()にだけ頼るのではなく、借用の概念を先に学ぶほうが正しいアプローチだと思う。借用こそが言語の核心Rustを学ぶ唯一の方法は、年収30万ドル以上の仕事が大量に出てくることだけだと思う。将来的にRustはquant分野でC++を置き換える可能性もあると思うが、すでにOCamlがあるし、もし極端に難しくて複雑な言語を学ばなければならないなら、まず金が見えないと厳しい。これまで最高年収の仕事はPythonだった
これらのコメントを見ていて、昔からのプログラマーが指摘を受けたときによく見せる態度がわかった。長く働くほど頑固になりがちだ。なぜコンパイラの提案を拒むようになったのか、それぞれ自分で考えてみるとよい。何を違う形でやりたいのか、何がそれを妨げているのかを自分で吟味すべきだ