日経クロステックが実施した「プログラミング言語利用実態調査2023」で「今後、スキルアップしたいと思う言語はどれですか」と複数回答可で尋ねたところ、トップ10の言語が分かった。それぞれの言語の特徴を解説する。
4位 Rust
多機能でC/C++並みに高速。ただし、難易度は高め
2015年に最初の安定版である「Rust 1.0」がリリースされたRustは、近年人気が高まっているプログラミング言語です。Rustの魅力は、高速に動くプログラムを、現代的なプログラミングテクニックを使うコードで作成できることです。これはプログラミング言語の歴史から見ても興味深い点です。
近年、プログラミング言語の進化の方向は、PythonやRubyのように実行速度を犠牲にする代わりに様々な機能を提供するか、Go言語のように提供する機能を絞って実行速度の向上を追求するかという2つの道に分かれていました。その中で、Rustは第3の道として、実行速度の向上をカリカリ追求しつつ、最新の機能を貪欲に提供していくという方向を示しました。この辺りが、Rustが人気になっている理由だと思われます。
Rustの実行速度は、おおむねC言語やC++に匹敵することが知られています。つまり、言語として最速の部類です。と同時に、「ミュータブル」(変更可能)なデータと「イミュータブル」(変更不可能)なデータの区別や、「パターンマッチ」、「代数的型」(Result型やOption型)など、様々な機能が用意されています。
難しい「所有権」のおかげで高速
一方、Rustは、C++以外ではあまり一般的ではない独特な概念が多いので、習得が難しいと言われています。例えば、Rustでは「所有権」という概念が非常に重要ですが、所有権を意識しながらプログラムをサクサク書けるようになるまでには、時間がかかると思います。
所有権とは、あるデータを所有するのはどの変数なのかを管理する仕組みです。所有権を保持する変数がもう使われないと判断されると、Rustの処理系(実行環境)はそのデータを削除します。この所有権の仕組みを利用することで、Rustではガベージコレクション(GC。Garbage Collection)なしでも効率的なメモリー管理を実現できています。
実行速度の点で、GCがないことは重要です。GCはメモリーを効率的に使うために、不要になったデータを自動的に削除する機能で、PythonやJavaなど、多くの言語が備えてきます。GCは便利な機能ですが、一方で、処理が遅くなったり、あるいは処理全体が一時的に止まってしまったりします。医療機器のソフトウエアなどでは、これは大きな問題です。GCによって、短時間とはいえ処理が突然止まってしまうのは困ります。
つまり、GCのない言語を使わなければならない分野があるわけですが、従来はC言語とC++くらいしか選択肢がありませんでした。近年は、そこにRustが加わったと言えます。ただし、C言語とC++では、メモリー管理はプログラマの仕事です。対して、Rustでは所有権を使ってRustの処理系がメモリー管理を行ってくれます。
コードを使って、所有権を説明しましょう。次の例を見てください。
このコードを実行すると「Goal!」と表示されます。細かな説明は省きますが、「let x = String::from("Goal!");」は、文字列を生成し、それを変数xに代入するコードです。次の「println!("{}", x);」は、変数xを標準出力に表示するコードです。
では、コードを次のように書き換えてみましょう。「let y = x;」を追加しています。
このコードを実行するとどうなるでしょうか? 「さっきと同じ結果でしょ?」と思うかもしれません。確かに、Pythonなどでの“常識”ではその通りです。ところがRustでは、「println!("{}", x);」でコンパイルエラーが発生します。
「let y = x;」は、Rustでは「変数xが持つデータの所有権を変数yに移動させる」という意味になります。よって、このコードの結果、変数xは何のデータの所有権も持たず、もはや使用できない変数になるのです。ですから、その後に来る「println!("{}", x);」で変数xを使おうとすると、エラーが発生します。
Rustでは「データの所有権を持てる変数は1つだけ」と決められていて、代入を行うと所有権が移動したと解釈されます。そして、所有権を持つ変数が使われないと判断されると、Rustの処理系は安心してそのデータを削除するわけです。所有権の移動は、関数の引数に渡しても発生します。
ただし、所有権の移動しかできないと、困る場面が出てきます。例えば同じ変数を2回関数に渡す場合などです。このような状況に対応するために、Rustは「借用」という機能を用意しています。借用では、所有権を渡すのではなく、代わりに「参照」という一種のアクセス権のようなもの渡します。
先ほどのコードを、借用を使って書き直すと次のようになります。
「let y = x;」ではなく、「let y = &x;」にしています。「&x」と書くことで、変数xが所有権を持つデータの参照を渡すようになります。
参照はいくつでも渡せます。また、通常の参照は読み取りのみで、値の変更はできません。
サンプルプログラム
リスト5はRustで記述した、いわゆるFizzBuzzのコードです(図3)。Rustっぽさを出すために、パターンマッチ(match)を使うサンプルプログラムにしています。FizzBuzzはいろいろな書き方がありますが、ここではmain関数と2つの関数(show関数とfizz_buzz関数)で書いています。main関数が最初に実行される関数です。
FizzBuzzの処理のメインとなるのはshow関数です。この中ではパターンマッチの機能を使って、3と5で割り切れる場合はFizzBuzzと表示し、3で割り切れる場合はFizz、5で割り切れる場合はBuzz、それ以外はその数字を表示するという処理を書いています。
fizz_buzz関数では、show関数をfor_each関数に渡しています。これは関数そのものを関数の引数に渡すという、関数型言語としての機能です。
ちなみに、リスト5では所有権の概念は登場していません。実は、所有権は型によってあったりなかったりします。この辺は、慣れないと難しいところです。