第1章 Rustの特徴

Firefoxの開発元であるMozillaが中心となって開発された。 不正なメモリ領域を指すポインタを許容しない、など安全性を重視している。 また、ガベージコレクションのような複雑なランタイムを持たない。 Stack Overflowのアンケートでは、「最も愛されている言語」で2016年から2018年まで3年連続1位となった。 コンパイラ・バックエンドとしては、LLVMが採用されている。 x86系Linuxを含むいくつかの環境では、外部ライブラリとの静的リンクもサポートしている。

導入事例についても見ていく。 Dropboxの分散ストレージシステム Magic Pocket では、 最初のバージョンが Go で書かれ、そのあと Rust で書き直された。 国内でもドワンゴが分散オブジェクトストレージ Frugalos を開発で採用した。 JavaScriptパッケージレジストリである npm のユーザ認証部分もRustで書かれている。 AWS は、サーバレスコンピューティングのためのセキュアな仮想マシン Firecracker を開発した。

第2章 はじめてのRustプログラム

Rustツールチェインは、Rustコンパイラ rustc、パッケージマネージャ cargo、 標準ライブラリ std からなる。 rustup を使うと stable版やnightly版といった複数のツールチェインを管理できる。 また、パッケージのトップディレクトリにrust-toolchainというファイルを作り、 nightlyと書き込んでおくと、そのパッケージではnightlyを利用できる。 nightly版には実験的な機能が含まれていて、毎晩リリースされる。 .rustupディレクトリはrustupによって管理され、.cargocargoによって管理される。 前者にはツールチェインの本体、後者にはrustccargorustupコマンドが配置される。 source ~/.cargo/envして使う。 cargo new --bin helloで新規パッケージを作成する。

println!のように最後に!がついているものはマクロなので、初期段階で評価・展開される。 x.func()は、メソッド呼び出し構文でありfunc(&x)と解釈される。 fn hoge<F>(..) where F: トレイト境界では、Fがwhere句で示されるトレイト境界を満たす型であれば、 どれでも指定できる。

デバッグには、直接GDBを使うよりもrust-lldbrust-gdbを使うのが便利。 互換性のないエディション(2015エディションと2018エディションの2種類)があり、クレート単位でCargo.tomlで指定できる。

muslターゲットを選択すると、外部ライブラリと静的リンクしたバイナリを作成できる。

第3章 クイックツアー

関数・変数・定数など識別子はスネークケース、型パラメータは英大文字1文字にするのが良い。 自動フォーマッタrustfmtがあるので、それを使うと便利。 エラーメッセージ対してrustc --explain 308で原因・対処法を調べることができる。

implブロックに、メソッドを実装していく。 クロージャは文脈によってFnFnMutFnOnceトレイトが自動的に実装される。 PartialEqトレイトやDebugトレイトは#[derive(Debug, PartialEq)]アトリビュートによって自動導出できる。

標準ライブラリとして、プラットフォームに依存しないマルチスレッドAPIが提供されている。 Rustコンパイラのコア開発者Nicholas Matsakis氏が開発したRayonクレートを使うと、 簡単・安全に並列データ処理を実現できる。 Syncトレイトを実装した型の値は、複数のスレッドから並列に使われても、必ずメモリ安全であることを意味する。

&mut x[..mid_point]ではxを2つの可変な借用に分割することはできないので、 かわりにsplit_at_mut()メソッドを使う。

第4章 プリミティブ型

言語にもともと備わっている型をプリミティブ型と呼ぶ。 ユニット型は、空のみを示す型で、値を1つだけ持ち、空のタプル()で表す。 整数リテラルはデフォルトでi32型と解釈されるが、0u8のようにサフィックスを付けると型を明示できる。 四則演算やシフト演算におけるオーバフローはリリースモードでは検出されない。 文字型はUnicode1文字を持つ。 配列の長さはコンパイル時に決まる。実行時に長さを変更したい場合はVec<T>型を使う。 配列に対してスライスのメソッドを使うと、暗黙的に型強制される。 strは文字数でなくutf-8のバイト数を返す。

第5章 ユーザ定義型

strは文字の追加・削除・変更ができないので、自由に変更するにはStringを使う。 不変のスライスとベクタの関係に似ている。 &を付けると型強制によって&Stringから&strに変換される。 また、Stringは実データを所有するが、&strはデータを所有せずに借用(参照)している。 let y = x as f64 / 2.5のように、asを使って型キャストを明示的できる。 ただ、asはスカラ同士の変換だけをサポートしているので、タプルや配列には使えない。 エラーハンドリングとしてtry!マクロが使われることがあったが、現在では?演算子の利用が推奨されている。

コンパイラの型チェック強化を目的に、タプル構造体を使った newtype というデザインパターンがある。

第6章 基本構文

コンパイルとリンクの単位をクレートと呼ぶ。 特別な扱いを受けるドキュメンテーションコメントがある。 ドキュメンテーションコメントには、///で始まる行、/***で始まるブロック、 //!で始まる行、/*!で始まるブロックが該当する。

関数の最後で、明示的にreturn文が置かれたコードはあまりない。 関数の途中で終了する場合のみreturn文を利用する。

Rustでは変数と値を紐づけることを「変数を値に束縛する」という。

右シフトは、符号なし整数に対しては論理右シフト、符号あり整数に対しては算術右シフトとなる。 パターンマッチング時に、パターンを連結するには|、範囲として指定するには...を使う。 if let式はmatchのシンタックスシュガーである。if let式はパターンが1つだけの場合と見なせる。

第7章 所有者システム

値にはただ一つの所有者が存在する。つまり、値の所有者はある時点において1人だけとなる。 値は、最後の所有者がスコープを抜けたときに、ライフタイムが尽きて破棄される。 破棄のタイミングで何らかの処理を実行したい場合には、Dropトレイトを通して、デストラクタを実装すればよい。

構造体や列挙体がCopyトレイトを実装していると、let p2 = p1のように記述したとき、 値はmoveされる代わりにcopyされる。例として、以下の型はCopyトレイトを実装している。

  • bool、char、i32などのスカラ型
  • 不変の参照&T型、
  • 関数ポインタ
  • すべての要素がCopyトレイトを実装したタプル型や配列型

新しい借用チェッカ NLL(Non-Lexical Lifetime)は、Rust 1.13.0かつ2018 Eiditionを選択したときに利用できる。 クロージャが実装するトレイトはFnFnMutFnOnceの3つがあり、外部の変数(自由変数)をクロージャ本体からどのように使うかによって決まる。

第8章 トレイトとポリモーフィズム

トレイト境界の書き方は3種類ある。

  • <P: Coordinates> のように型パラメータの直後にトレイト名を続ける
  • where P: Coordinatesのように関数の型のあとに記述する
  • func_name(point: impl Coordinates)のように引数の位置に記述する

いつくかの標準ライブラリのトレイトは#[derive(XXX)]アトリビュートによって自動的に実装できる(自動導出)。 例えば、CloneCopyDebugDefaultEqHashOrdPartialEqPartialOrdは 自動導出できる。

第9章 パーサを作る

Rustではcrates.ioにパーサ支援ライブラリが公開されている。 既存のパーサライブラリとして例えば以下の2つがある。

  • nom:マクロベースのパーサコンビネータライブラリ
  • combine:関数ベースのパーサコンビネータライブラリ

第10章 パッケージを作る

クレート、パッケージ、ワークスペース、プロジェクトの用語を整理しておく。

  • クレートは1つのRustプログラムを指す。いくつかのモジュールから構成される。コンパイルの結果、実行ファイルやライブラリが生成される。
  • パッケージはCargoの1単位。複数のクレートから構成される。
  • ワークスペースは、複数のパッケージから構成されるプロジェクトのこと。
  • プロジェクトはCargoの最大単位。

Cargoはlibクレートのエントリポイントとしてlib.rs、binクレートのエントリポイントとしてmain.rsを認識する。 #![deny(missing_docs)]を付けると、ドキュメントの記述を強制しLinterでチェックできる。 tests直下にテストを配置すると、Cargoは自動的にテストクレートとしてコンパイルする。 コード中のドキュメントブロックもテストされるので、ドキュメントが陳腐化するのを防げる。 テストの網羅度のチェックにはkcovやtarpaulinなどのツールを使うとよい。 crates.ioに公開したものは原則削除できないので、慎重にアップロードする。

第11章 Webアプリケーション、データベース接続

PostgreSQLやMySQLを使うときには、Dieselを使うと安全で拡張可能なORMやクエリビルダを実現できる。

第12章 FFI

RustからCの関数を呼び出すには、そのシグネチャをextern "C"ブロック内に記述する。 また、libcクレートはC言語のlibcで定義されている関数や型がカバーしている。 プロジェクト直下にbuild.rsを置くと、ビルドスクリプトして認識される。