とっかかりに「歯車本」を読んでみましたよ
Go言語に入門したり会社のミニ勉強会で各種プログラミング言語の歴史の話をしていたら、だんだんRust言語も気になってきました...ということで実際に書いてみる前に本を1冊読んでみることにしました。
現在日本語で読めるRust言語の本は10冊ほどありますが、情報リソース集にもよく出てくる入門本の定番本。2020年8月に発売された新しめの本です。界隈では「歯車本」の愛称で親しまれています。Amazonでの星の数も本書が一番多いですね。
厳密にいうと表紙に歯車が一部でも見えるのは『詳解Rustプログラミング』『基礎から学ぶ 組込みRust』もあるのですが、「歯車本」と言ったら本書になるようです。
- とっかかりに「歯車本」を読んでみましたよ
- Chapter1 プログラミング言語 Rust
- Chapter2 環境構築
- Chapter3 Rustの基本
- Chapter4 プログラムを作成する
- Chapter5 Webアプリケーションの開発
- Chapter6 WebAssembly
- Chapter7 GUIアプリケーション
- Chapter8 組み込みシステム
- Chapter9 開発ツール
- Chapter10 プロダクトをリリースする
- Chapter11 いろいろなRustの発展的Tips
- まとめ:Rustの世界に一通り入門できる本
Chapter1 プログラミング言語 Rust
最初の3章は「入門」ということでRust言語の概要を語っていきます。Stack Overflow Developer Surveryでも2016年から5年連続1位と執筆時点で書かれていますね。読んだ2022年も1位だったので通算6年、今後も首位をキープしそうです。
- とにかく実行速度が速い:機械語に直接コンパイル、ガベージコレクションなし、抽象化した機能を使ってもコストなしの方針を貫いた「ゼロコスト抽象化」で速度はC/C++に匹敵。
- モダンな言語機能搭載:変数はデフォルト不変。関数型プログラミングスタイルのイテレータ操作。これも関数型プログラミングスタイルの代数的データ型と
switch
式の代わりにmatch
式。関数の中では変数は強力な型推論。interfaceの代わりにトレイト(trait)
を使って構造体に振る舞いを追加できる。 - 幅広く実装可能:OS製作可能、組み込みなど低レイヤーからWebアプリまでなんでも。Webフレームワークは
actix-web
が有名。 - ツール群の充実:ライブラリ管理+ビルドは
Cargo
でお任せ、エディタのサポートも豊富、標準フォーマッタrustfmt
、標準リンターclippy
が公式で用意。モダンな言語らしい充実。 - 安全性の強力な担保:ガベージレクションがない代わりに、メモリ安全やスレッド安全を言語機能でガード、コンパイルで弾いてくれる。安全性を重視。
- エディションという考え方:2015エディションと2018エディションがあり、互換性はないがクレート(ライブラリのこと)単位で切り替えられる。
最後には海外本の"Rust in Action"の翻訳本の『詳解Rustプログラミング』にも載っている利用事例として、GAFAクラスのいろんな企業で導入の話が増えていることも述べられています。
最初にベンチマークのグラフが載っているのですが確かに一番下、実行速度がCとC++に匹敵しているんですね。メモリ安全に苦しめられた人類の夢がいま叶おうとしている...! そしてこのグラフだとGo言語がC#やJavaとあまり変わっていなくて、あれJavaより高速なんじゃなかったっけ?と思ったりしました。
Chapter2 環境構築
環境構築周りです。
- The Rust Playground でブラウザ上で試せる。
- 環境構築はRustツールチェーン(Cargo, rustc, rustupの総称)の中のインストーラー
rustup
を使えばすぐインストールできる。
環境設定も爆速、Cargoが神ツールというのはよく見聞きしますが、やはりこのへんはモダンな言語らしくしっかり整っていますね。
Chapter3 Rustの基本
基本文法の章。
- 数値型は種類が多い。
- 文字列スタイルの
str
と、標準ライブラリで定義された文字列型String
が別にある。 let tupple = (1, "1");
のように異なる型を収められる集合が「タプル」。tupple.1
のようにドットでアクセス。let arr: [i32, 2] = [0,1];
のように[]で宣言するのが配列。サイズ固定。arr[0]
のようにアクセス。struct Person {}
のように構造型を定義、enum KeyType {}
のように列挙型を定義。- 標準ライブラリで列挙型の
Option
、列挙型のResult
、vec![]
マクロで作る要素数増減可能の配列がVec
、値をメモリのスタック領域でなくヒープ領域に保存してくれるBox
が用意。この辺をよく使う。
Go言語だと関数の2つ目の戻り値で返ってきた結果をだいたい次のif文で毎回判定するのがちょっとめんどいなと思ったのですが、そのへんが改良されている印象です。
let
とmut
で変数の可変を決定。const
の定数はビルド時に置き換わり、static
な定数はバイナリファイルの特定位置に配置。if
は「式」なので、評価結果を変数に入れたり関数の引数にできる。- 繰り返しは
loop, for, while
の3つ。break
するときのラベル指定はラベル名先頭に'
がいる。 for
ループでfor(1..5)
のようにRange
型が使える。switch
文の代わりにより強力なmatch
がある。列挙型で分岐が抜けていたらコンパイルエラーで弾ける。break;
もいらない。パターンでアンダースコア_
はワイルドカード扱い。自作の構造体に
Iterator
トレイトを適用、Item
に紐づけてnext()
メソッドを追加しておけばfor文の中の繰り返し処理で使える。fn sampleAdd(a: i32, b: i32) -> i32 { }
形式で書くのが関数定義。関数の最後でセミコロンなしで記述された値が自動でリターンされる値になる。このへんRubyぽい。impl 構造体名 { }
で構造体にメソッドを加えてオブジェクト指向的にできる。戻り値を&Self
にしてメソッドの最後でself
をリターンすると、person.say_yoo().say_woo();
のようなメソッドチェーンができる。マクロが色々用意されていて
format!("{}", "出力するよ");
のようにマクロ名の最後に!
をつけて実行。使い方はほぼ関数と同じ。unimplemented!()
やtodo!()
などの実装補助用のマクロも、関数の中に書く。それとは別にこの構造体には指定のメソッド定義が必要...とかを書く
#[derive]
アトリビュートがある。これは構造体の前の行に書き、アノテーションぽい。trait トレイト名 {}
の中にそのトレイトで実装が必要なメソッドを書いたり、定義済みのメソッドを書いたり。そしてimpl トレイト名 for 構造体名 {}
で実装、ポリモーフィズムを実現している。関数名<T, S>(t: T, s: S) -> (T, S) {}
のようにしてT
型、S
型に任意の型が入るジェネリクスが書ける。呼び出し側では型推論が効くので<>
内は書かなくて良い。a = 1;
で変数は値に「束縛」される。b = a;
のようにして値の所有権が移る。(ムーブセマンティクス) コピーセマンティクスもある。some_calc(&b);
のように引数を参照で書くと、値へのアクセスが可能に。所有権は元のbにある。値の参照を渡すのを「借用」という。- 不変な参照は幾つでも渡せる。
mut
で宣言された可変な変数の参照は1つだけで、違反するとそこでコンパイルエラー。 y = &b;
としたら、元の所有者bの変数の生存期間が終わったあとでyを使うことはできない。これが「ライフタイム」で、違反するとそこでコンパイルエラー。thread::spawn( ここに||を使ったクロージャ );
でスレッドを作ったら、スレッドの外側で宣言した変数はスレッドの中で使うと寿命が長くなるかもしれないのでコンパイルエラー。move
キーワードで所有権を移す必要がある。- 複数のスレッドがスレッド外の同一の変数(共有のデータ)にアクセスする際は、そのまま書くとコンパイルエラー。その共有データは排他制御を行ってくれる構造の
Mutex
を使うと大丈夫。 - スレッド同士で通信するメッセージパッシングには
mpsc:channel
関数を使ったチャネルを使う。 - 非同期処理はRust1.39から
.async
.await
が導入された。
Rustの学習コストが高い原因と言われる束縛やライフタイムの話がここでキター!となりました。本書は文法3割実践7割の本なのでそれほどページを割いてはいないのですが、参照周りや変数の生存期間を厳密に判定するということでなんとなく分かったかな?という気になりました。それに実装時はコンパイルエラーが弾いてくれるので大丈夫そうではあります。
スレッド処理もGoのゴルーチンほど簡単ではないですが専用の仕組みが用意されていますね。そして自分的には非同期処理がすごく難しく感じました...理解しきれていない気がします。
- 他言語のライブラリやパッケージ、RubyのジェムをRustでは「クレート」と呼ぶ。
- クレートの中を構成する一段階小さな構成要素が「モジュール」で、複数のソースコードの集まり。
> cargo search
で探せ、> cargo add
すれば導入済みのcargo-edit
がCargo.toml
の中を書き換えてくれる。このへんはJavaScriptのnpmでpackage.jsonを書き換えてくれるのと同じ雰囲気。> cargo new --libs
プロジェクト名 でクレートが作れる。- 1ファイル内に
pub mod module_a { 関数1, 関数2... } mod module_b { 関数1, 関数2...}
のようにインデントして関数を定義すると複数モジュールがかけるが、通常は1ファイル=1モジュール。 - 1ファイルに
pub mod module_b { 関数1, 関数2...}
を書いたmodule_b.rs
の他に同位置にmodule_b/
を掘ってその中に別モジュールを書いたり。 - 外部から使う時はファイルの先頭で絶対パス的に
use クレート名::モジュールB::モジュールC;
のように書く。相対パス的に書く時はsuper
やself
も使える。
クラスがないので1ファイル=1クラスではなくて1ファイルに複数の構造体や関数が入って良いんですね。このへんは若干独特な感じがします。
- ビルドシステム&パッケージマネージャーの cargo にもいろいろコマンドあり。
> cargo new プロジェクト名
でプロジェクトディレクトリ作成。- すでにあるディレクトリに行うには
> cargo init [オプション] [パス]
> cargo build
でプロジェクトを全部ビルド、target/debug や target/release/ に出来上がる。> cargo check
でエラーチェック。ビルドするよりバイナリファイル生成がない分速い。> cargo run
でビルドが必要ならビルド、その後実行してくれる。> cargo test
でテストコード実行。> cargo fix
でcheckを裏で実行した後、直せるものはソースコードを自動修正。> cargo clean
で生成物を消してお掃除。> cargo doc
で target/doc/ にドキュメントを生成。> cargo install クレート名
で公開されているクレートをインストール。> cargo uninstall
でアンインストール。> cargo search
で公開されているクレートを検索。> cargo publish
で自作クレートを公開。- ルート直下にある
Cargo.toml
内にもセクションが色々あって情報が記述。ビルド時はCargo.lock
ファイルも生成。中は修正不要、バージョン管理したほうがよい。
そしてユニットテストもGo言語と同じで最初から用意されていました。
- 対象の関数と同じファイルに、
#[test]
アトリビュートを追加してtest_元関数名()
を作成。中でassert!()
などを使って判定。同一ファイルだと外部に非公開な関数でもユニットテストできる。 - パニックを発生させるテストや、
> cargo test
時にオプション指定しないと実行されない普段は無視するテストもかける。 - クレート用のプロジェクトを作ると、自動で
#cfg[(test)]
アトリビュートをつけたmod tests
というtests
モジュールが作成され、この中に書く形になる。このtests
ディレクトリは別のクレートの位置づけになる。 - これらのテストコードは
> cargo test
したときしかバイナリコードに含まれない。
Goだと対象のコードと同じディレクトリに _test.go ファイルを作って書いていきましたが、Rustだとテスト対象と同じファイル内に書いて関数直前のアトリビュートで判定してくれるんですね。このへんなかなか独特です。
Chapter4 プログラムを作成する
ここからは「Part2 実践編」として実際の開発へ。コンソールアプリケーションとしてまずは「逆ポーランド記法」、情報処理試験などに時々出てきたような気のする「1+1」を「11+」と書くアレを入力にすると答えが出力される rpncalc アプリケーションを作っていきます。
- 標準で
std:env:args()
を使って実行時の引数は取れるが、順番諸々を考えるとクレートのclap
を使うのがグッド。本書時点では3系がまだ開発中。柔軟に使える。オプション引数を表す構造体の前にderive
マクロを指定する記述方式もある。 - 入力のファイル読み取りはだいたい定形処理だが、内部関数
run
に「トレイトBufReadを実装した任意の型」を取れるように<>
で指定する工夫がいる。 - 逆ポーランド記法のロジックは構造体RpnCalculatorを作ってトレイトで関数を追加していく。インスタンスを作るメソッド名は
new
にしてRpnCalculator::new(true)
のようにコンスタクタっぽく呼べるが、newなのは言語仕様ではなくてただの慣例。 - イテレーターをコレクションに変換してくれる
collect()
関数で型を指定するときに::<>
と書くのを「ターボフィッシュ」と呼ぶ。 - ユニットテストはこれまでのように
main.rs
の同じファイルの最後に#[cfg(test)]
でmod test {}
の中に書いていく。test_*()
関数が実行時に認識される関数で、その中にassert
系を書いていく。 - RustはGoと同じく例外がなく、2つめの戻り値ではなく
Result<T, E>型
の構造体を使うのが一般的。今回のアプリでは正常終了時は数値、エラー時は文字列の形で定義。呼び出し元ではmatch
式で正常時とエラー時の処理を2行に分けて書ける。 Result
型を返す関数の中では、直前の処理の結果がErr(e)
だったらそこで返すという?
演算子が使える。- メソッドチェーン的に関数をつなぐ中で、
Result<T, E>
なら直前の結果がOk(t)
の場合のみ実施、Err(e)
の場合のみ実施...というやり方を|t|
で書ける。 - 独自のエラー定義がちょいめんどくさいので、自作アプリで完結する際に使う
anyhow
, 外部から呼ばれる場合を考えたthiserror
という定番のクレートがありお勧め。 - ファイルパスは正確には
String
型でなくPathBuf
型がありOS間の差異を吸収してくれる。Rustは文字列問題を解消するため多くの文字列型を持っている。 - 最後は
Cargo.html
にアプリ名を書いて、cargo install --path .
でその場所にパスが通る。
愚直に関数の2つめの戻り値についてif文を書くであろうGo言語と比べるとエラー処理はスマートな感じがしました。独自のエラー処理も色々工夫されているのですね。日本語文字列で問題が起きにくくなっているというのもさすが21世紀のモダンな言語な感じがします。
Chapter5 Webアプリケーションの開発
この章ではこちらも定番、Webアプリを作っていきます。Rustで最も有名らしいWebアプリケーションフレームワークの actix-web
、この中で非同期処理を行う actix-rt
を入れて開発していきます。他のフレームワークには rocket, warp, tide
などがあるそうです。
- 以下のように関数の前の行のアトリビュートでGETやPOSTのパスを指定、非同期で関数を定義してレスポンスを書くのが基本。
#[get("/")] async fn index() -> Result<HttpResponse, actix_web::Error> { Ok(HttpResponse::Ok().body("Webアプリだよ")) }
- main関数にはポート8080でサーバーを起動するよ...というのを書く。この2つの関数がある
main.rs
だけで、もうWebサーバーとしてhttp://localhost:8080/
で動いてしまう。 - エラーハンドリングは
thiserror
クレートを使って戻り値に使えるようにするとよい。 askama
というHTMLテンプレートエンジンのクレートを入れる。templates/index.html
のようなファイルにHTMLを書く。中の{% %}
や{{ }}
の中にプログラム側のコードを書く。Python のjinja テンプレートの流れを受け継いでいる。- 繰り返し要素の構造体を定義したらアトリビュートで
index.html
と対応しているよ... というのを書いて、let html = IndexTemplate{ entries}
のように構造体からスタート、変数html.render()
するとレスポンス文字列ができあがる。 - データベースは今回はSQLite。DB全般のコネクションプールのクレートである
r2d2
, SQLiteへの接続に使うクレートのr2d2_sqlite
を入れる。 - DB名を指定してマネージャーを作り、そこからコネクションを作り、
execute()
関数でSQL文を実行。このへんは他の言語とだいたい同じ。プリペアドステートメントを作って変数を入れたり。 - SQLライブラリとしては
diesel
が最も有名で、テーブル定義もマクロで書けたり、SQL文を書かずにRustのメソッドチェーンでほとんどのDB操作ができる。Active Recordパターン的な感じ? - POSTのリクエストのパラメーターを自動でパースするために
serde
クレートを使う。derive
という機能も使う。 - 最後はDockerイメージ作成方法も。イメージビルド高速化のテクニックもある。
- コラムによるとAWSのLambda用のクレートもあり、index()関数をサーバーレスで動かしたりもできるとのこと。
HTMLテンプレートやDBアクセスなど、だいたい他の言語と同じような感じでイメージが湧きました。レスポンスのHTML生成処理のスタートが構造体から始まるのがちょっと面白い感じがしました。エラー処理がRustの特徴を生かして共通化されています。
サンプルはTODO管理アプリなのですがTODOの追加や削除のメソッドなのですが、第1引数が画面からのパラメーター、第2引数がDBとのコネクションを持ったマネージャーなんですね。継承がある言語だったら共通の親クラスでこのマネージャー的なものを持ったりするところですが、Rustだと関数の引数で常に渡していくわけですね。
サンプルなので最終的なコードもmain.rs
の大きな1ファイルの中に入っているのですが、実際には分割していくでしょうからこのあたりの定石も知りたいなと思いました。
またサーバー起動時に同時にCREATE TABLE文を流してtodoテーブルがなければ作成する方式になっています。こういう動作確認用のサンプル向けの処理もできるんですね。
あと非常にどうでもいいのですが、actix-web
というネーミングに昔のMicrosoftの悪名高い技術であるActiveXをつい連想してしまいました。(インターネット老人会!)
Chapter6 WebAssembly
フロントエンドの未来を語る際によく出てくるけどなんかまだ実現するのは先の感じのしていたWeb Assembly
、略してwasm
の実装例が解説されています。これはありがたいですね。
- ブラウザ上で動く新しい言語であり使用のwasm、主要ブラウザは対応済み。JSを完全に置き換えるのではなく重い処理をwasmに頼むようなイメージ。wasmの実行ファイルを作るには...
- JSを扱うのでnpmはいる。プロジェクトをテンプレートから作れるようにする
cargo-generate
をcargo install
でインストール、RustとwasmとJSをつなげるwasm-pack
というタスクランナーを入れる。カニさんのフェリスが帽子をかぶったロゴがある。こちらはただのクレートではないのでcurl
コマンドで入れる。 cargo-generate
でGitHubのURLを指定して定形ファイルが多いテンプレート一式をダウンロードしてプロジェクト作成。wasm-pack
でビルドするとHTML部分が生成される。- 比較対象としては描画に負荷が掛かる「マンデルブロ集合」を使う。X軸Y軸の位置と繰り返し回数を指定し、ロジック自体はRustでもJSでも書ける。
- HTMLの中に
<canvas>
要素を複数用意。JSでこの要素の中に書いていく。 - Rust側でも追加クレートなど幾つか準備が必要。最終的に
#[wasm_bindgen]
というアトリビュートを付加したRust側の関数が、JS側から同じ関数名で呼べる。 - いろいろやって実施、JSのみで実装した場合と比較するとRustは描画にかかる時間が2/3程度と1.5倍高速。しかしあまり変わらない場合もある。wasmとJS間のデータ連携に掛かる時間もネック。
- 連携するデータ量が少なく、計算量がより大きいタスクの方が差が出るはず。ということで「ナンバープレース」という9x9マスのパズルを例に取った場合も実装例を上げて比較。繰り返し回数による差異やばらつきが大きいが、本書で設定したケースではwasmのほうが掛かる時間が1/10ぐらい〜4/5ぐらいと速い。しかしJSのほうが速いケースもあり、JS側のJITコンパイラがいい仕事をしたのかも。
このwasmの話は前から実体のイメージが沸かずにいたのですが、本書のおかげでだいぶ解像度が上がりました。DOM操作から何から何まで他の言語に置き換わってJSもReactもVue.jsも突然すべてオワコンになるのではなくて、フロントエンドがやっていた仕事の中で負荷のかかる一部の処理だけを置き換えるイメージ、そして言語の選択肢の中ではいち早くwasmに対応して直接コンパイルでき、C/C++並の実行速度を誇るRustが一番の注目株...ということなんですね。
「マンデルブロ集合」も「ナンバープレース」も中身のロジックはまったく分からなかったのですが(笑)よくこんな題材がぱっと出てくるなあと思います。
そして結果を見るとだいぶばらつきがあり、流石のRustでも処理時間1/100〜1/10クラスまで安定して圧倒的に速くなるわけでもないんですね。動かすまでの準備もけっこうな手間がまだかかる印象です。本書にあるように、wasmはまだこれから成熟していく段階の技術なのだなあと感じました。
Chapter7 GUIアプリケーション
Rustの世界ではGUIのクレートは一つには決まっておらず、幾つかあるものが方向を模索している状況のようです。
- 有名なものは
GTK, Iced, Conrod, imgui-rs, OrbTk
。本章では開発が盛んなIced
を紹介。 - 概念的にはState, Message, Viewロジック, Updateロジックという分け方がある。
- 開発方法としては関係クレートを
Cargo.toml
に追加した後、普通にRustのコードで書いていく。ウィジェットもいろいろある。 - コード内でttfファイルを相対パスで指定してフォントを読み込んだり。
- 非同期処理を実行してMessageを送信できる
Subscription
という仕組みがある。
使う機会はあまりなさそうなので流して読んだのですが、こうやって作るのか...という感じ。ここでも最終的なコード一式はsrc/main.rs
にまとめられています。実際のところはどう分割していくのでしょうか。
Chapter8 組み込みシステム
C言語と同等の性能がありGCがないので実行時の不確定要素も少ないRustは組み込みにも向いている...ということで実践していく章。CPUの写真もあったりして自分的には未知の領域です。
- クロスコンパイル用の環境構築をして、エミュレーターの
QEMU
を導入。 - コンパイルのターゲットを変えるために
project/.cargo/config
ファイルを作る。またビルド時に読み込むファイルを指定したbuild.rs
というファイルがいる。 main.rs
でも通常と違うところがあり、標準ライブラリを使用しないことを示す#![no_std]
、main関数を使用しない#![no_main]
、代わりにスタートする関数を指定する#[entry]
、またその関数は戻り値を返さないことを意味する!
を()
の代わりに使ったり。- ビルド時にエミュレーターが動くようにスクリプトを作っておくと良い。
- 通常使わないクレートとしては、
Vec
やString
もそのままでは使えないのでメモリアロケーターを自作する際に使うalloc
, 最大容量を宣言してから使うコレクションのheapless
などがある。
これが低レイヤーの世界なのか...!という感じでした。書いているのは同じRustのコードなのにだいぶ雰囲気が違うのですね。
Chapter9 開発ツール
開発時の周辺ツールを巡っていく章。
- 本格的なプロジェクトディレクトリ構成。
example/
やbenches/
があるのが他の言語と違いそう。 - 大多数のユーザー向けにクレートの中の一部の機能だけを有効化してコンパイル時間を短縮させる「フィーチャ」という機能がある。
- クレートにバグを見つけた時にGitHubにアクセスして直していく方法。
- 複数のクレートをまとめられるワークスペースという機能。
- Cargoのサブコマンド類。ファイル変更を
watch
できたりする。 - フォーマッタとしてはRust標準のものが
rustfmt
。> cargo fmt
とするだけで自動生成してくれる。 - Rust標準のリンターが
clippy
。> cargo clippy
で怪しいコードを指摘してくれたりする。 - コードカバレッジの
cargo-tarpaulin
。(これは名前はなんと発音するのでしょうね) - Rust 1.44ではnightly限定だが、
cargo bench
というベンチマーク測定のツールもある。サブコマンドいろいろ。 - CIではGitHub Actionsにも定義済みのアクションがある。
フォーマッターを公式がもう提示してくれていて無用な論争を避けられるのはGo言語と同じですね。なおRustのインデントは伝統のスペース4文字でした。
Chapter10 プロダクトをリリースする
リリース時のオプション指定などを解説する章。
- 全てのライブラリを静的にリンクしたシングルバイナリを作るには、
musl
というツールがターゲットごとに用意されているのでrustup
で入れておくとよい。 - ある特定のバージョンのリリースで不具合が発生した時、ビルドを再現するために
Cargo.lock
とrust-toolchain
というファイルがある。 - デバッグビルドは最適化レベルが0だが、他に幾つかオプションがある。
- 一度プログラムを実行してからその結果をもって高度な最適なをしてくれる Profile-Guided Optimizationという機能がある。
- クレートの脆弱性データベースがrustsec.orgで管理されている。
cargo-audit
を入れとくと、コマンドを打てば使っているクレートの脆弱性がチェックできる。 - 自動的に生成したデータでプログラムがクラッシュしないか検証するテスト手法「ファジング」を実行するための
cargo-fuzz
というツール。 - クレートを作ってもpublishできないようにする方法もある。
流石に最高速度を誇るRust、最適化周りもいろいろあるのですね。脆弱性データベースが最初から用意されているのもモダンな言語的です。
Chapter11 いろいろなRustの発展的Tips
最後はPart 3として、知っておくとよいことやお勧めしたいことを詰め合わせたTipsという章があります。ここがとてもお役立ちでした。
- マクロにも幾つか種類がある。
- 宣言的マクロ:別の場所で
macro_rules!
で宣言、関数のように使う。Cの文字列変換されるマクロに近いがより安全。 - 手続きマクロ:コードを受け取りコードを生成する。
- 関数的マクロ:宣言的マクロより複雑な定義ができる。Rust 1.45で使えるようになる予定
- deriveマクロ:構造体や列挙型の前の行で
#[derive(Debug)]
のように指定するマクロ。型名を取れるようにしたり。 - アトリビュートマクロ:関数の前の行でも
#[attribute_name]
のようにつけられるマクロ。実行の前後でログを出したり。
- 宣言的マクロ:別の場所で
- 他の言語からRust、Rustから他の言語w呼ぶ際に使える
FFI:Foreign Function Interface
という仕組み。事前準備が必要だが関数名でコールできる。 panic
の使い方。想定内のエラーはErr、プログラムのバグはpanicで。普段はpanicの挙動を意識しなくてよい作りになっており、上級テクニックあり。unsafe { }
の中でメモリ安全性が保証されない領域のコードを書ける。深堀りした解説。- エディションの使い方。2015、2018の他に2021も予定されている。
- Rustで作られた高速なOSSの紹介。
- Rustのコミュニティの話。日本最初のカンファレンスは2019年に開始で盛り上がってきた。
- 競技プログラミングの世界でも、実行速度と言語機能からRustを選ぶ人が増えてきている。
- Rustを仕事で取り入れるヒント。小さく始めて社内に仲間を増やしたり、Rustで作ることを宣言してアピールしたり。
panicやunsafeの深ぼった話などかなり難しいのですが、さすがに奥の深い言語です。
最後にコミュニティの話もあり、今後機運が高まっていく言語なので盛り上げていきたいという気持ちが感じられます。こうしたコミュニティで活動している方々でも、普段の仕事は別言語でかつRustを追っているという方も多い...ということで、やっぱり日本ではこれから広めていきたい立ち位置の言語なのだなあと思いました。
まとめ:Rustの世界に一通り入門できる本
概要や文法も最初で解説、実践メインでコンソールアプリからWebアプリにWeb Assemblyの話、組み込み、応用的な話...と一通りまとまった本でした。他の言語を知っているソフトウェアエンジニア、プログラマーの方が新しくRustに入門するなら評判通り本書がお勧めかと思います。
なお元から文法3割、実践7割ぐらいを目指したとのことで、Rust言語仕様の突っ込んだ細かいところはあまり説明されずに進むところもあります。そのため読み進めながら「あれこのワード初めて出てこなかったっけ?(そして後のほうで説明されていたりする)」と戸惑うところも若干ありました。まあこのへんは文法をより大きく扱った他の本で補強をということでしょう。
Rust言語自体については...学習コストが高いと恐れられて(?)いますが流石に手ごわいですね。噂の所有権やライフタイム周りはコンパイルエラーを潰しながら進めば何となく分かりそうですが、他の諸々も含めるとやはり骨太で難しいと感じました。だからこそそれを乗り越えた先に人類をempowerさせる大きなポテンシャルを持っているのでしょう。今後の盛り上がりが楽しみな言語です。
著者の方々のブログ記事です。