Rのつく財団入り口

ITエンジニア関連の様々な話題を書いているはずのブログです。

【感想】『実践Rustプログラミング入門』:歯車本でRustの世界へ

とっかかりに「歯車本」を読んでみましたよ

 Go言語に入門したり会社のミニ勉強会で各種プログラミング言語の歴史の話をしていたら、だんだんRust言語も気になってきました...ということで実際に書いてみる前に本を1冊読んでみることにしました。
 現在日本語で読めるRust言語の本は10冊ほどありますが、情報リソース集にもよく出てくる入門本の定番本。2020年8月に発売された新しめの本です。界隈では「歯車本」の愛称で親しまれています。Amazonでの星の数も本書が一番多いですね。
 厳密にいうと表紙に歯車が一部でも見えるのは『詳解Rustプログラミング』『基礎から学ぶ 組込みRust』もあるのですが、「歯車本」と言ったら本書になるようです。

『実践Rustプログラミング入門』- 歯車本で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 new でプロジェクトディレクトリ作成、> cargo run ですぐ実行。
    • Cコンパイラが見つからない環境ではgccがいたり。(WindowsだとMicrosoft C++ Build Toolsが必要になる)
    • 主要エディタもサポート。
    • Rust Language Server(rls)をインストールする。
    • 代替でrust-analyzerという言語サーバーも開発が進んでいる。現在α版。
    • 設定ファイル Cargo.toml の修正を簡単にしてくれるcargo-editというツールもある。

環境設定も爆速、Cargoが神ツールというのはよく見聞きしますが、やはりこのへんはモダンな言語らしくしっかり整っていますね。

Chapter3 Rustの基本

 基本文法の章。

  • 数値型は種類が多い。
  • 文字列スタイルのstrと、標準ライブラリで定義された文字列型Stringが別にある。
  • let tupple = (1, "1"); のように異なる型を収められる集合が「タプル」。 tupple.1 のようにドットでアクセス。
  • let arr: [i32, 2] = [0,1]; のように[]で宣言するのが配列。サイズ固定。arr[0]のようにアクセス。
  • struct Person {} のように構造型を定義、enum KeyType {} のように列挙型を定義。
  • 標準ライブラリで列挙型のOption、列挙型のResultvec![]マクロで作る要素数増減可能の配列がVec、値をメモリのスタック領域でなくヒープ領域に保存してくれるBoxが用意。この辺をよく使う。

Go言語だと関数の2つ目の戻り値で返ってきた結果をだいたい次のif文で毎回判定するのがちょっとめんどいなと思ったのですが、そのへんが改良されている印象です。

  • letmutで変数の可変を決定。
  • 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-editCargo.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;のように書く。相対パス的に書く時はsuperselfも使える。

クラスがないので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-generatecargo install でインストール、RustとwasmとJSをつなげるwasm-packというタスクランナーを入れる。カニさんのフェリスが帽子をかぶったロゴがある。こちらはただのクレートではないのでcurlコマンドで入れる。
  • cargo-generateGitHubの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]、またその関数は戻り値を返さないことを意味する !()の代わりに使ったり。
  • ビルド時にエミュレーターが動くようにスクリプトを作っておくと良い。
  • 通常使わないクレートとしては、VecStringもそのままでは使えないのでメモリアロケーターを自作する際に使う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.lockrust-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プログラミング入門』- 歯車本でRustを学ぼう

まとめ:Rustの世界に一通り入門できる本

 概要や文法も最初で解説、実践メインでコンソールアプリからWebアプリにWeb Assemblyの話、組み込み、応用的な話...と一通りまとまった本でした。他の言語を知っているソフトウェアエンジニア、プログラマーの方が新しくRustに入門するなら評判通り本書がお勧めかと思います。
 なお元から文法3割、実践7割ぐらいを目指したとのことで、Rust言語仕様の突っ込んだ細かいところはあまり説明されずに進むところもあります。そのため読み進めながら「あれこのワード初めて出てこなかったっけ?(そして後のほうで説明されていたりする)」と戸惑うところも若干ありました。まあこのへんは文法をより大きく扱った他の本で補強をということでしょう。

 Rust言語自体については...学習コストが高いと恐れられて(?)いますが流石に手ごわいですね。噂の所有権やライフタイム周りはコンパイルエラーを潰しながら進めば何となく分かりそうですが、他の諸々も含めるとやはり骨太で難しいと感じました。だからこそそれを乗り越えた先に人類をempowerさせる大きなポテンシャルを持っているのでしょう。今後の盛り上がりが楽しみな言語です。

著者の方々のブログ記事です。

blog-dry.com

matsu7874.hatenablog.com

nitamago.hatenablog.com