目次
Rust の利用例
CLI ツールの作成
サーバーサイドでの利用
WebAssembly をターゲットとした開発
Rust 初心者がつまずきやすいポイント
コンパイルエラー
「代入」の意味の違い
貸し借りに関するトラブル
Rust を学ぶための資料
書籍
チュートリアル
レファレンス
コミュニティ まとめ
フリーランスの収入見込みをチェック
言語
経験年数
予想収入
?¥610,000月あたり
フリーランスを続けた場合の収入
?1年後
¥700,000月あたり
2年後
¥750,000月あたり
Rust の利用例
さまざまなプレーヤーが Rust を使って製品やサービスを開発しています。代表的な例には、Dropbox や npm、Mozilla があります。この 3 社はサービスのバックエンド開発に Rust を利用しています。また Mozilla はウェブブラウザ Firefox の開発に Rust を利用しています。この 3 社以外の事例は Rust 公式サイトの "Production Users" で紹介されています。
国内にも Rust を利用するプレーヤーは存在します。Cookpadや CADDi、 FORCIA、OPTiM、Idein などが代表例のように思います。
Rust の主なユースケースは次の 3 つにまとめられるように思います。
- CLI の作成
- サーバーサイドでの利用
- WebAssembly をターゲットとした開発
CLI ツールの作成
Rust の利用例を集めた Awesome Rust というサイトには、多くのコマンドラインインターフェース(CLI)ツールが紹介されています。その中には馴染み深い UNIX のコマンドを置き換えるものもあります。次の表にあるプロジェクトが、その代表例になるように思います。
| 名前 | 同等の機能を持つ UNIX コマンド |
|---|---|
| exa | ls |
| bat | cat / less / more |
| fd | find |
| ripgrep | grep / rgrep |
これらのコマンドは、Rust のパッケージ管理ツールである cargo でもインストールできます。cargo は Rust の開発環境をインストールする時に、コンパイラーなどと一緒ににインストールされます。
cargo はプロジェクトのビルドツールでもあります。プロジェクトビルド時に、必要なライブラリのダウンロードやビルドも自動的に行います。Rust の標準ライブラリだけでなく、サードパーティーのライブラリのビルドも同様です。
サードパーティーのライブラリは、crate.io と呼ばれるレポジトリーで公開されるのが一般的です。プロジェクトマニフェストに使用するライブラリの名前とバージョンを記述しておくと、cargo がビルド時に crate.io から適切なものをダウンロードしてビルドします。
cargo は Rust のプログラムをシングルバイナリとしてビルドします。依存するライブラリの追加やライブラリパスの設定といった作業をしなくても、バイナリファイルをコピーするだけで CLI ツールを利用できるようになるのは、ツールの配布という面では大きなアドバンテージとなるように思います。
サーバーサイドでの利用
上であげた国内外のプレーヤーの多くは、サーバーサイドで Rust を利用しています。データベースサーバーや、プッシュサーバーそのものを Rust で実装するといった使い方もあれば、ログの集計といったバッチ処理を Rust で実装する場合もあります。
- デプロイが簡単なこと
- コンパイラのチェックによって、安全なプログラムが書けること
- スケールが簡単なこと
ツールの配布が簡単であるということは、サーバーサイドでも有利に働きます。特にライブラリのバージョン違いによる不具合を回避しやすいという点は、運用面でも有利になるように感じています。
コンパイル時のチェックと安全性
ビジネスロジックを実装することの多いサーバーサイドプログラムでは、安全性はさらにに重要です。適切な引数が指定されているか、初期値は設定されているか、操作しようとしているデータは存在しているのか、データの変更は意図的なのか、といったプログラムを安全にするためのチェックがビルド時に行われます。
このチェックで発見される問題の例が、null ポインターへのアクセスです。Rust では Option のような型を使って、null となりうる値が表現されます。型情報をヒントに、コンパイラーは null ポインターのアクセスが起こりそうな箇所を発見します。コンパイルエラーという形でプログラマーに注意を与えることで、事前に null ポインターへのアクセスにともなう問題を回避しようとします。
並列処理もより安全に
並列計算が安全にできるのも、スケーラビリティが必要なサーバーサイドでは有利です。Rust では基本的にメッセージパッシングを利用して並列計算を実装します。このモデルでは、データはメッセージを通じてスレッド間を移動します。そのためスレッド A で行われた変更が、スレッド B の持っているデータを意図せず変更する、といった問題が起きにくくなります。
一方で、Rust は共有メモリもサポートします。パフォーマンスが特に重要な場面は、こちらのモデルを用いることになると思います。Mutex や Arc といった型を利用して、ロックが取得されているか、アトミックオペレーションが実行されているか、データはスレッド間で共有できるのか、と言ったチェックがビルド時に行われます。これにより共有メモリを、より安全に利用できます。
WebAssembly をターゲットとした開発
サーバーサイドプログラム、特にサーバーレスの文脈で最近注目されているのが、WebAssembly です。これは実行ファイルの形式の一つで、ソフトウェアモジュールを CPU アーキテクチャ非依存な形で表現できます。元は C 言語などで書かれたプログラムを Web ブラウザ上で実行するために設計されましたが、既存のソフトウェア資産を安全に利用できる特徴からサーバーサイドでの利用も進んでいます。
柔軟性と柔軟性の両立をねらった WebAssembly の採用
サーバーレスの環境では、実行環境が提供する API を利用して処理を実装します。使い慣れたプログラミング言語とライブラリを使って柔軟に開発をしたいという開発者側のニーズがある一方で、安全に、そして高速にプログラムを実行させなければならないというサービス提供者の制約も存在します。柔軟性と安全性、そしてパフォーマンスと言った要件を満たしやすい、という理由で WebAssembly を採用する例も増えてきています。
例えば、Shopify ではビジネスロジックを実装する「アプリ」に WebAssembly を使用できますし、Istioや、Fastly、CloudFlare などでも WebAssembly が利用できます。
Rust は WebAssembly を公式サポート
Rust は WebAssembly のサポートが手厚いように思います。専用のワーキンググループ を持っており、公式のドキュメントや多くのツールを提供しています。
またサポートの手厚さは、WebAssembly が x86_64-unknown-linux-gnu や aarch64-apple-darwin のようなコンパイルターゲットとして公式にサポートされていることにも現れているように思います。コンパイルターゲットへの追加も、ツールチェーンの管理を行う rustup というコマンドで簡単に行えます。またビルドもコンパイルターゲットを指定する以外は、通常のビルドと同様に行えます。
Rust 初心者がつまずきやすいポイント
Rust 安全性やパフォーマンスと言った良い面が語られる一方で、勉強の難しさについても語られることが多いように思います。ここでは Rust 初心者、特に他の言語での経験がある人がつまずくであろう点を 3 点取り上げます。
- コンパイルエラーの量に圧倒される
- 代入の意味の違いに戸惑う
- 貸し借りのトラブルに巻き込まれる
つまずきポイント:コンパイルエラーに圧倒される
Rust はプログラム上の問題を可能な限りビルド段階で見つけようとします。そのおかげでビルドされたプログラムは、一定の安全性が担保されています。これはコンパイルエラーが比較的多く出ることも意味します。
「とにかく動かしてみたい」という気持ちを抑えてコンパイルエラーを修正することになります。慣れるまでのストレスを感じる方もいらっしゃるかもしれません。
Rust のエラーメッセージは単にエラーを伝えるだけでなく、開発者にとって有益でわかりやすくあろうとしているように感じます。次のエラーはパターンマッチでチェックされるパターンに抜けがあるため発生しました。エラーが発見された場所や、その内容がわかりやすく説明されているように感じます。
error[E0004]: non-exhaustive patterns: `None` not covered
--> src/main.rs:3:25
|
3 | let message = match value {
| ^^^^^ pattern `None` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `Option
error: aborting due to previous error
For more information about this error, try `rustc --explain E0004`.
また Rust コンパイラーである rustc には --explain というオプションがあります。上記のエラーメッセージにもあるように、このオプションをつけて rustc を実行すると、エラーに関するより詳しい解説を読めます。なお同じ内容が Rust Compiler Error Index に公開されています。エラーメッセージを使って、こちらのページを検索することでも、解説を読むことができます。
つまずきポイント:「代入」の意味の違い
Rust では a = 1 のような操作のことを代入ではなく、束縛と呼びます。代入は用意した領域に値を書き込むといった意味を持つことが多いのに対し、束縛は値に名前をつけることを意味します。最初の条件は束縛に関する制約で、次のコードのように、値が別の変数を束縛した後、元の変数を参照しようとすると発生します。
let a = format!("Hello, world");
let b = a;
println!("{}", a)
変数は束縛されることで値の所有権を獲得します。そしてその値が別の変数を束縛することで、所有権を失います。値が保存されているメモリー領域の解放の判断に所有権は利用されます。例えるならモノの管理責任を明確にすることで、勝手に使われたり、いつまでも捨てらずに残って部屋がモノであふれるのを防ぐといった仕組みです。
代入のつもりで行った操作によって意図せず値の所有権が移動してしまい、文法上は正しく見える部分でコンパイルエラーが起きてしまう、といったことが初心者の心をくじく場面も多いように思います。他の言語では代入操作を表す記号が、Rust では異なる意味で使われていることが混乱を大きくするむきもあるように思っています。
個人的には「束縛」と明示的に呼ぶことで、代入とは違う操作なんだとしばらく意識づける期間がありました。これが意外にうまくいったように思います。
つまずきポイント:貸し借りに関するトラブル
関数呼び出しやオブジェクトの作成、イテレーターを使ったループ処理、そしてタイプ量を少なくするために短い名前の変数に一時的に代入する、といったことで所有権は移動します。これを緩和するために借用という概念があります。所有権を一時的に貸し出すことで、所有者以外もモノを利用できるようになる仕組みです。
借りたモノをすぐ返すことができるなら、そして誰も又貸ししないなら、貸し借りにまつわるトラブルは起きにくいでしょう。そうもいかないのが現実です。また長期間の借用は、管理責任をあいまいにしがちです。長く借りていたモノを引き上げられたため業務が止まるといった悲劇もあると聞きますが、同様のことがプログラムで起きた場合、クラッシュやセキュリティホールの原因となります。
このような悲劇を防ぐのがライフタイムという概念です。Rust コンパイラーは、データのライフタイムを計算し、削除後にデータを利用する場合はエラーを出力します。
所有権とライフタイムは初学者が戸惑うコンパイルエラーの代表的なものだと思います。どちらもデータと、それが記憶されているメモリー領域の管理に関する概念です。データを変更できるのは誰で、そのデータを読めるのは誰か、あるデータを消していいのか悪いのか。これらの判断を行うために利用されます。
所有権とライフタイムに関するエラーは、次の 3 ルールを理解するとわかりやすくなるように思います。
- ある値に束縛される変数は最大 1 つに制限される
- 変数のスコープから外れた場合、その変数を束縛する値は削除される
- データ構造の属性値は、データ構造そのものよりも長い時間存在しなければならない
所有権とライフタイムは Rust の基本的な概念です。思いもしない場面で出会い、ストレスを感じられるかもしれません。コンパイルエラーが多くて面倒と感じる時もあるでしょうが、コードを丁寧に見てくれるレビュワーが近くにいるといった気持ちで、気長に付き合ってゆけば Rust らしいプログラムが書けるようになってゆくと思います。
Rust を学ぶための資料
書籍、公式サイト、ブログ、スライド、さまざまな形で Rust 入門のための資料が公開されています。ここでは書籍、チュートリアル、そしてレファレンスとして使えるサイトを紹介します。入門書籍の紹介
まず書籍を 2 冊ご紹介します。
実践Rustプログラミング入門
実践Rust入門[言語仕様から開発手法まで]
どちらも実践的な入門書をうたっており、基本文法から実践的な例までカバーされています。前者は文法自体の解説を短くまとめ、実践例を多く見ることで Rust を解説しているのに対して、後者は文法に関しても細かい点までカバーされています。
チュートリアル
Web 上のチュートリアルも 3 つ紹介します。
1 つめのものは開発環境のインストールからはじまって、crate.io で公開されているライブラリを利用した簡単な CLI ツールを作るところまでをカバーしています。cargo の使い方や、プロジェクトフォルダの構成についても説明されていますので、何もない状態から Rust の開発を体験するには良い内容のように思います。
2 つ目のチュートリアルも、1 つ目と同じく開発環境のインストールからはじまります。エラー処理や、ジェネリクス、モジュールシステム、テストといった話題もカバーしており、1 つ目のチュートリアルの後の学習に利用されると良いでしょう。
"Command line apps in Rust" も CLI ツールの作成を題材にしています。Rust の文法を学ぶというよりは、コマンドライン引数の解析、エラーの取り回し、設定ファイルの読み込み、シグナルハンドリングと言った、より実践に近い内容となっています。Rust の文法を一通り抑えた方や、比較的分量のあるコードを書きながら必要に応じて文法を確認するといった学習スタイルの方向けのコンテンツのように思います。なおこちらのコンテンツは英語でのみ提供されています。
レファレンスとして使えるサイト
レファレンス的に文法を確認できる資料は、次の3 つです。1 つ目は言語仕様の解説です。2 つ目と 3 つ目はサンプルコードを用いた解説となっています。
Rust by Example にあるサンプルコードの右上には、▷ボタンが設置されています。このボタンをクリックすると、サンプルコードがサーバー上でコンパイルされ、実行されます。エディターにコピー&ペーストしなくても、気軽に振る舞いを確認できます。
rust-jp:日本の Rust コミュニティ
最後に日本の Rust コミュニティである rust-jp を紹介します。公式ドキュメントの和訳や、Slack を使った質問への回答も行っています。オープンで、初心者にも優しいコミュニティとなっていますので、気後れせず参加いただけたらと思います。
rust-jp は数ヶ月に 1 度の頻度で、LT会を開催しています。使ってみた系の話題から、深い内容まで多様なテーマの発表を聞くことができます。初心者の発表も歓迎していますので、発表を目的に勉強されるには良い会のように思います。
まとめ
以上Rust の特徴と、参考資料を紹介しました。紹介した特徴をまとめると、次のようになるかと思います。
- cargo / crate.io を中心としたエコシステムが整備されていること
- デプロイが簡単なこと
- 型システムと所有権、ライフタイムを利用した静的解析によって安全なプログラムが書きやすいこと
- 必要に応じて、メッセージパッシングと共有メモリを使い分けられること
- WebAssembly のサポートが手厚いこと
Rust は、さまざまな点で配慮が行き届いている言語のように感じています。コンパイルエラーの多さなど戸惑う点もあるかもしれませんが、既存のプログラミング言語、特に C 言語や C++ での経験をお持ちの方は、その利点を早くから感じることができるように思います。この記事が Rust の勉強をはじめてみよう、とお考えの方の参考になれば幸いです。