Web開発者としての興味、関心に基づきRustを端的に紹介し、その強みや弱みについて理解を深める本連載。第1回では、Rustを採用するモチベーションとは何かを整理、考察します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
近年、プログラミング言語「Rust」に関する重要なニュースを多く見るようになりました。例えば以下のような記事です。
Microsoft、「Rust for Windows v0.9」を公開
実装言語を「Go」から「Rust」に変更、ゲーマー向けチャットアプリ「Discord」の課題とは
これらの記事が指し示すことは、いわゆる「GAFAM」(Google、Amazon.com、Facebook、Apple、Microsoft)と呼ばれる米国主要IT企業および先進的なWebベンチャー企業がプロダクションユースとしてRustを採用し始めており、さらに継続的投資意欲があることを示していると考えられます。他にもRustの勢いを示すものとして以下のような記事があります。
「Rust」はなぜ人気があるのか、Stack Overflowがユーザーのコメントを紹介
Stack Overflowにおける最も愛される言語の1位にRustが君臨しています。2016年から2020年まで5年連続でRustが1位をキープし続け、さらには2020年の結果では非常に勢いのある言語である2位のTypeScriptに19ポイントという大差をつけて1位を獲得しており、エンジニアコミュニティーにおいても極めて勢いがある言語であるように感じられます。
これらは単なる一過性のブームなのでしょうか。手慣れた既存言語ではなく、新しい言語であるRustを利用、学習するのはなぜでしょうか。第1回ではRustを採用するモチベーションとは何かを整理、考察します。
Rustはその開発のいきさつからして、C/C++からの移行が意識されています。Rustが出現するまでは、C/C++はハイパフォーマンス(最速、最高効率)なアプリケーションを開発するならば避けて通れない言語だとされてきました。一方でC/C++では、言語規格に動作の定義がない命令を記述することができてしまいます。その命令の結果生じる動作のことを、未定義動作と呼びます。未定義動作を引き起こす命令の典型例としては以下のようなものがあります。
未定義動作の典型例は(厳密には予測不能ですが例えば)以下のようなものがあります。
// deref_null_pointer.c #include <stddef.h> int main(void) { int *null_pointer = NULL; int value = *null_pointer; // 未定義動作発生 }
// deref_null_pointer.rs fn main() { let p: *const i32 = std::ptr::null(); // null pointerそのものは安全である unsafe { // unsafeで囲まないとコンパイルエラー println!("{}", *p); // 未定義動作発生 } }
未定義動作を引き起こすプログラムは安全とは言えません。例えば、セキュリティ問題や障害を引き起こす可能性があります。未定義動作が生じないことをC/C++コンパイラは(警告は出せますが)保証しないので、プログラマーがコードを確認し保証する必要があります。未定義動作は、コンパイラによる最適化などにより予期せぬ振る舞いや非決定論的な振る舞いを生じることも多くデバッグも困難なものになりがちです。言語仕様に精通したプログラマーの尽力による問題解決が必要になります。
Rustは、unsafeという言語機能を用いない限り未定義動作を引き起こし得る命令を記述しようとしてもコンパイル時または実行時エラー(パニック)になります。もしパニックとなったときでも決定論的に振る舞うのでデバッグがしやすく、なおかつC/C++に比肩するパフォーマンスを同時に有しています。
※この記事では上述の「未定義動作を引き起こす命令」のうち1、2、3、4のような命令から保護されている、あるいは適切な動作が規定されていることをメモリ安全、5のような命令から保護されていることをスレッド安全と呼ぶことにします。
Rustはその開発のいきさつからC/C++との比較を通じて語られることが多いですが、Web開発においてC/C++を採用することはまれで、メリットと呼ばれていることがそんなに大したことではないように思えるかもしれません。
実際のところ、Ruby、Python、JavaScriptといった典型的なWeb開発言語を用いる限り、メモリ管理を言語処理系に委譲できるためuse after freeを気にする機会はありません。また、配列の範囲外アクセスをすると実行時エラーにはなりますが言語に備わっている例外機構により守られます。動作は言語の定義する範囲内に収まり、決定論的な振る舞いを示すことが多いので、未定義動作を相手にするのに比べるとデバッグするのも比較的容易です。
Rustと典型的なWeb開発言語の安全性や開発生産性に関するさらなる考察は次回以降改めて検討することにして、ここではパフォーマンスに着目することにします。
伝統的なWebアプリケーションは計算時間よりもネットワークI/Oがボトルネックになることが多く、複雑なデータクエリ処理はデータベースに処理を委譲するよう設計することが基本とされています。そのため、Webアプリケーション自体の計算パフォーマンスはそれほど重視されない傾向にあります。そしてそれが比較的低速な言語でWeb開発してもよい理由であり、C/C++が採用されてこなかった理由だと考えられます。
一方でWeb開発の文脈において計算パフォーマンスが改善されるとどのようなメリットがあるのかを思い付く範囲で列挙してみます。
安全性とパフォーマンスを両立させるRustを使用することによって、「サービスの提供価値」「サイトリライアビリティ」「ユーザーエクスペリエンス」「運用コスト」など、今日のサービス開発における重要なファクターを安全性を確保しながら改善していけることが期待できます。一方で開発生産性への影響などは客観的に語ることがかなり難しいですが、次回以降検討を深めます。
この世にはすでにたくさんのプログラミング言語があります。その中でRustの卓越性、あるいは特異性とも呼ぶべき要素について主観的な説明を交えて紹介します。
Rust以前の代表的な言語はメモリ安全性とGC(ガベージコレクタ)が不可分なものとして扱われており、メモリ安全性を得るためにはGCを使わなければならないという関係にありました。でなければ、人間がメモリ安全であることを保証する必要があります。
一方でRustは「所有権システム」と呼ばれるモデルを採用し、コンパイル段階でメモリ安全性を検証することで、GCがオプトアウトできることを示しました。さらにその所有権モデルはコンパイル段階でのスレッド安全性を検証することにも利用できて、データ競合も合わせて検出します(これは実に驚くべきことで、私がRustを学習し始めた重要なモチベーションの一つになります)。
所有権システムがRustの卓越性の根幹となるものと筆者は考えていますが、同時にRustは現代的な言語であり、さまざまな点において妥協のない言語設計になっていると強く感じます。ドキュメンテーション、依存性管理、テスト、エラーハンドリング、合理的な構文――これらのトピックに関する明確な答えをRustは提示していて、それらもまたRustの卓越性を構成しています。
第1回では、Rustへのモチベーションと題して、Rustの魅力やWeb開発者目線でのRustのメリットについて提示しました。第2回では、PythonとRustの構文比較をしながら、パフォーマンスについて簡易なベンチマークを取ることを予定しています。そのままパフォーマンス比較をすると自明で退屈な結果になるため、Python側も少し工夫をしてもう一歩踏み込んだ比較をしていきます。
1988年生まれ、大阪府枚方市出身
京都大学工学部電気電子工学科卒、同大学エネルギー科学研究科修了
応用情報技術者・情報処理安全確保支援士試験合格者
YKK AP株式会社にて超高層建築物の外装設計に従事し、型・モジュール設計・ウオーターフォールプロセスに精通する。その後ITエンジニアに転向。paiza株式会社にて、Ruby on RailsやVueを用いたWebサービスのスクラム開発に従事、現在に至る。
最も得意な言語はPython、最も影響を受けた言語はClojureであり、シンプルな関数型(的書き方ができる)言語を好む。関数型的記法を持ちながら、実行性能が高いRustに興味を持ち研さんを続けている。
Copyright © ITmedia, Inc. All Rights Reserved.