世界を動かす技術を、日本語で。

C++からRustへのフレーズブック

概要

C++からRustへの移行を支援するフレーズブック。 C++の一般的なパターンをRustで表現する方法を具体例で解説。 エンジニアリング上のトレードオフも高い視点で議論。 必要な章をすぐに参照できるランダムアクセス設計。 Brown University Cognitive Engineering Labの専門家による執筆。

C++ to Rust Phrasebookの概要

  • C++プログラマー 向けにRustの学習を支援する書籍
  • C++のパターンRustのイディオム に変換する方法を解説
  • 各パターンごとに 具体的なコード例設計上の議論 を掲載
  • 前から順に読んでも良い が、 必要な部分だけ参照 する使い方を推奨
  • C++ならできるがRustでの書き方が分からない」ときに対応する章を参照
  • Brown University Cognitive Engineering LabC++/Rust専門家 による手書き執筆
  • 正確な情報適切な詳細レベル を重視
  • AIによる自動生成文無し、すべて人間による執筆

利用方法と特徴

  • 新章追加のアップデート 通知を希望する場合、メールアドレス登録が可能
  • Rust未経験者 には「 The Rust Programming Language」や「 Learn X in Y Minutes」の先読推奨
  • 組み込みシステム開発者 には「 The Embedded Rust Book」との併用を提案
  • Rustonomicon」や「 Learn Rust With Entirely Too Many Linked Lists」と比較し、 C++との対比 を重視した内容
  • Rustの内部動作 よりも C++の視点からRustを理解 できる解説

フィードバックと参加方法

  • 各ページ下部に フィードバック用フォーム へのリンクを設置
  • 誤字脱字や事実誤認 などの指摘を歓迎
  • 各章末の クイズ回答匿名で研究目的に保存
  • このページへのフィードバック は専用リンクから送信可能

その他リソース紹介

  • Rust初心者向け :「 The Rust Programming Language」、「 Learn X in Y Minutes
  • 組み込み開発者向け :「 The Embedded Rust Book
  • Rustの深い理解 を目指す場合:「 Rustonomicon」、「 Learn Rust With Entirely Too Many Linked Lists

Hackerたちの意見

エピック!これめっちゃいいね。

自分の経験から言うと、よくある落とし穴の一つは、慣れている言語を合わない言語で書こうとすることだね。ある言語に合うイディオムを、別の言語に無理に当てはめようとしても、うまくいかないことが多い。よく見る例としては、Cの考え方を持っている人がC++のクラスにinit()関数を書くこと。確かに動くけど、C++のやり方はコンストラクタでやることなんだよね。(C++のイディオムに対する例外を挙げようとする人は、最後にしてね、ありがとう!)Cの考え方だと「メモリを割り当てて、値を設定する」って感じだけど、C++では割り当てと初期化が一つのアクションにまとめられてる(プログラマーの視点から見ると)。じゃあ、C++の考え方を持った人がRustを書くときの落とし穴は何だろう?この「フレーズブック」は、C++の考え方をRustに応用するアイデアを受け入れてるけど、確かに多くの状況でうまくいくと思う。でも、Rustでやるには間違ったC++のフレーズって何だろう?

こういうリソースは、二つの言語の近いところと遠いところを話し合うのにいい場所だね。もちろん、それぞれの言語の中にもスタイルの違いがたくさんある。

確かに、君が言ってる初期化メソッドのパターンには理由があるんだ。C++のコンストラクタは値を返せないからね。もしコンストラクションが失敗する可能性があるなら、エラーを伝える唯一の方法はC++の例外を使うことなんだ。例外を受け入れるコードベースならそれでいいけど、(C++の)例外ってあんまり良くないから、そうじゃないコードベースも多いし、代替手段を探さなきゃいけなくなる。ここ数年、私はコンストラクタをプライベートにして、オブジェクトの構築を静的メソッドで行うパターンをどんどん採用してるんだ。これ、実際にはRustに近い感じだよ。

二相初期化が大嫌いだ。C++ライブラリが、bare bonesのCライブラリをextern "C" { }でラップしてるのとか、malloc()/free()とか、Cスタイルのコーディングとか、本当にイライラする。

一番の落とし穴は、Rustの参照がポインタと同じだってこと。実際にはポインタとして実装されてるけど、その役割は静的に知られたスコープに制限された一時的(しばしば排他的)なアクセスを提供することなんだ。これはかなり特定的で、ポインタやC++の参照の一部の使い方にしか合わない。C++ではポインタはコピーを避けるために使われるけど、Rustの参照はデータを保存したり保持したりしない。これらの目標が重ならないと、「十分に長く生きていない」という恐ろしいワック・ア・モールにハマっちゃうんだよね。

理由としては、C++の開発者がイディオムに沿わないRustを書く方が、安全でないC++をずっと書き続けるよりマシってことだと思う。だって、unsafeを使って借用チェッカーを完全に回避しない限り、やっぱり安全性は高いからね。完璧を求めて良いものを敵にしないって感じ。しかも、イディオマティックなRustの定義はそんなに厳密じゃないし。Clippyが簡単なことについては大体ガイドしてくれるし、他の部分は必ずしも従う価値があるわけじゃない。トレイトを「正しく」使おうとする人は、結局は複雑さが増しちゃうことが多いよね。

元の意図を逆転させて、これは素晴らしい。Rustがどんな風に改善しているのかを学んで、可能ならそれを使ってC++をより良く書こう。

Rustを少し学んだ後でも、主にC++を書くつもりのC++の実践者たちが、実際に役立つ洞察を得たと言っているのをたくさん見たよ。委員会の提案書でも「RustはXをする」ってのが、XがPythonのような言語にとって現実的な選択肢であることを示すための良い反論になっている。Pythonはパフォーマンスにあまり気を使わず、重いランタイムやガベージコレクタを抱えることもあるけど、C++のような「本物」の言語でも同様だよ。コードがC++の文字列処理コードに「code.contains("FOO")」を追加した論文がその例で、これをする言語の長いリストがあるけど、Rustのこともちゃんと言及している。

これを使ってRustとC++の両方を学んで、どっちが好きか見てみるつもり。

見てみたけど、確実にC++の知識が必要だね。少なくとも、両方に同じくらいの時間をかけているから、C++の足元をすくうようなことには警告できない。

これはあんまりチュートリアルや学習ガイドって感じじゃないね。C++をRustにざっくりパターンマッチさせたルックアップテーブルみたいなもんだ。学び始めるにはあんまり良い場所じゃないと思う。

C++にはいろんなバリエーションがあって、このガイドは自分の価値を十分に伝えられてないと思う。C++ではメンバー付きのenumを簡単に実装する方法があって、クラスや構造体の中に匿名のenumを入れればできるんだけど、実際には「できない」とされてる。Rustのモジュールについて話すときに、C++のモジュールの存在を完全に無視するのも同じ。現代のツールによってサポートされてるのにね。他にもテキストに関して似たような問題があって(コンパイラフラグを追加すれば同じ動作が得られるのに、「Rustはそれを箱から出してすぐにやる」って議論するのはやめてほしい。全コードベースを書き直す必要があるのに、ビルドシステムにちょっとしたテキストを追加するだけで済むのは全然同じじゃない)、FFIについてはあまり議論されていなかった。一般的には「それ用のクレートがあるし、なければ教えて」って感じだけど、私の経験では、エソテリックなことに関してはクレートはあまり良くない(グラフィックス関連やffmpegもそう)し、制限されたC++で書く方がずっと楽だよ。Rustにはハッピーパスがあって、徐々に広げているけど、Rustが存在する前のC++には長い歴史があって、それを簡単に片付けるのは難しい。

C++モジュール、まだVSCodeでサポートされてないのか。5年前のスレッドから通知が来るたびにイライラするわ。[0] 0: https://github.com/microsoft/vscode-cpptools/issues/6302

1990年代からCやC++のコミュニティにいる者として、長期的にはRustもこの現象から逃れられないと思ってる。数十年にわたるプロダクションコードの中でトップ10に入った他のプログラミング言語と同じようにね。ハッピーパスはどんどん曖昧になっていくよ。さまざまなバックグラウンドや自分のアジェンダを持った人たちが、エコシステムでコードを書くことの意味について意見を持つようになるから。ITがアップグレードを拒否する企業や、業界の実装が増えること、最新情報を追わない教師たちもいるし…。

C++モジュールは今、実際にプロダクションレディなの?最後にチェックしたときは、主要なコンパイラ全てでちゃんとサポートされてなかったけど。

C++ではメンバーを持つ列挙型を実装する簡単な方法があって、クラスや構造体の中に匿名の列挙型を入れるだけなんだ。可能だけど、できないとマークされてる。これがC++で列挙型のメソッドに対して正にそうしてるガイドだよ。メンバーについては、これって「enum」のすべてのインスタンスがすべてのフィールドを持つことにならない?それってRustの列挙型とは本当に比較できないと思うけど、そういう意味で言ってるのかな。

ほとんどの例で、Rustのバージョンがより冗長だって気づいた。

C++の構文と意味はC++のイディオムに最適化されてるけど、Rustはそうじゃないんだ。RustはC系やOOPよりもML系の言語に近いから、C++のイディオムを「エミュレート」する必要があるんだよね。逆も同じで、Rustの列挙型のパターンマッチングに相当するものは、C++では冗長で扱いにくくなる。

それに、C++版は本来よりも冗長になっちゃってる。普通の人はこう書くだろうね: class Person { int age = 0; }; Rustもデフォルトの構造体フィールドの値をこんなに簡単に書けるようになればいいのに。

トレイトとクラスの議論では、大きな違いを見落としてる - Rustのトレイトには関連データがないし、親トレイトのデータにアクセスすることもできない。Rustでオブジェクト指向プログラミングをしようとすると、すぐに混乱しちゃうんだ。これって、RustでC++を書くときには大きな問題になる。所有権については全く触れられてないし。 > Rustの多くのライブラリは、ResultやOption型を返すAPIと、パニックを引き起こすAPIの2つのバージョンを提供していて、エラーの解釈(予期された例外的なケースかプログラマーのバグか)を呼び出し側が選べるようになってるんだ。え?普通、Rustでは結果を返す関数呼び出しに対して、パニックを起こしたいなら .unwrap() や .expect() をつけるだけだよね。一般的に言えば、RustとC++の違いは、命令型コードの書き方ではなく、データ構造の間の接続をどうレイアウトするかに関係してる。Rustの難しい問題のほとんどは所有権の設計に関するものだし、C++でも同じことが言えるけど、プログラムが実行時にクラッシュするまで気づかないんだよね。