C++の悩みのほとんどはRustで解決する


Rust を勉強し始めて1年半ほどになりました。
勉強しているうち、C++のデバッグで悩まされる事の多くは、Rustではコンパイラが解決してくれている!と思ったので、紹介します。

※できるだけ平易な言葉を使って、大事なポイントをうまく伝える事に重きを置いて書いています。よって表現として正確ではない部分もあるかも知れませんが、ご了承ください。

対象読者:C++ 中上級者で、Rustに興味がある方

お悩みその1:「このメモリはいつ確保されていつ開放されるの?」

ある程度C++の経験がある人なら、既存のソースコードをデバッグする時、「このメモリはいつ確保されていつ開放されるのか?」と気にしますよね。そしてそれがわからない場合は、

  • ソースコードを目で追って、メモリの状況を頭の中で想像しながら確認する。
  • デストラクタでブレークポイントを仕掛けて、実際にプログラムを実行してみる。

こんなことをするんじゃないでしょうか。
shared_ptrを使っていたとしても、その参照カウントがどこで増えてどこで減るのか、気にする必要があります。
つまりC++では、メモリの確保と開放は、実行してみないと分からない状況になっています。

Rustでは、「オーナーシップ(所有権)」という機能により、コンパイル時にメモリの確保と開放のタイミングをすべて決定できるようになっています。
よってメモリの確保・開放のタイミングを気にする必要はありません。

参考:
https://doc.rust-lang.org/book/first-edition/ownership.html (英語)
http://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/ownership.html (日本語)

お悩みその2:「この変数はいつ誰によって変更されるの?」

C++では、ある変数の書き換えを制限したい場合、クラスのprivateを使うか、constを使うかです。
それ以外の変数については、いつどこから書き換えられるか、管理する方法はなく、プログラマーの頭の中で考えるしかありません。

デバッグするなら、

  • 変数名で全検索して、書き換えられる可能性のある箇所を探す
  • ウォッチポイントをセットして変数がいつ書き換えられるかを調べる。

こんなことを良くやっているのではないでしょうか。

Rustでは、コンパイラに「これからこの変数を書き換えるよ」と伝える事ができます。そして、ある変数の書き換え権限は、常に誰か1人しか持つことが出来ません。(複数の場所から同じ変数を書き換える様なコードはコンパイルエラーになる)
この制約により、プログラムがすっきりと分かりやすくなります。

参考:
https://doc.rust-lang.org/book/first-edition/mutability.html (英語)
http://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/mutability.html (日本語)

お悩みその3:「boost使ってみたけど、謎のエラーが大量に出る!」

boostにはテンプレートを使ったライブラリが多くありますが、テンプレートにまつわるコンパイルエラーは、慣れていないと何が悪いのか全くわからないことが多いです。

Rustでもジェネリクス(=テンプレート)が使えます。そして、型引数に対して、トレイトという制約を掛けることができます。
型引数がトレイトによって示された条件を満たしていない場合は、エラーとなります。
このトレイトをうまく使うことによって、ジェネリクスを使う側に利便性が生まれます。

※C++でも、type_traits を使って、型引数に対する制約をつける事は出来ます。

参考:
https://doc.rust-lang.org/book/first-edition/generics.html (英語)
http://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/generics.html (日本語)

まとめ

以上、ざっくりと、C++での悩みどころがRustでは解決されている事を紹介しました。
Rustをやり始めると、上に挙げた制限の所為で、「こんな事でコンパイルエラーになるの??」と戸惑うことが多くあると思います。
最初は、コンパイルエラーとの戦いにうんざりするかも知れません。
でもそのうち、Rustさんが、危険なコードを書かない様に僕を導いてくれている!と感じられるようになるはずです。

また、制限をあえて回避する方法も用意されています。
unsafeでくくれば、生ポインタも使えますし、RefCell/Cellを使えば、書き換え権限のチェックをコンパイル時でなく実行時に行ってくれる様になります。
どうしてもという場面では、このような回避策を使えば、C++と同じ感じでコーディングもできるはずです。

これを機会に、Rustの勉強を始めてみてはいかがでしょうか。