第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
によって管理され、.cargo
はcargo
によって管理される。
前者にはツールチェインの本体、後者にはrustc
、cargo
、rustup
コマンドが配置される。
source ~/.cargo/env
して使う。
cargo new --bin hello
で新規パッケージを作成する。
println!
のように最後に!
がついているものはマクロなので、初期段階で評価・展開される。
x.func()
は、メソッド呼び出し構文でありfunc(&x)
と解釈される。
fn hoge<F>(..) where F: トレイト境界
では、F
がwhere句で示されるトレイト境界を満たす型であれば、
どれでも指定できる。
デバッグには、直接GDBを使うよりもrust-lldb
やrust-gdb
を使うのが便利。
互換性のないエディション(2015エディションと2018エディションの2種類)があり、クレート単位でCargo.toml
で指定できる。
muslターゲットを選択すると、外部ライブラリと静的リンクしたバイナリを作成できる。
第3章 クイックツアー
関数・変数・定数など識別子はスネークケース、型パラメータは英大文字1文字にするのが良い。
自動フォーマッタrustfmt
があるので、それを使うと便利。
エラーメッセージ対してrustc --explain 308
で原因・対処法を調べることができる。
impl
ブロックに、メソッドを実装していく。
クロージャは文脈によってFn
、FnMut
、FnOnce
トレイトが自動的に実装される。
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を選択したときに利用できる。
クロージャが実装するトレイトはFn
、FnMut
、FnOnce
の3つがあり、外部の変数(自由変数)をクロージャ本体からどのように使うかによって決まる。
第8章 トレイトとポリモーフィズム
トレイト境界の書き方は3種類ある。
<P: Coordinates>
のように型パラメータの直後にトレイト名を続けるwhere P: Coordinates
のように関数の型のあとに記述するfunc_name(point: impl Coordinates)
のように引数の位置に記述する
いつくかの標準ライブラリのトレイトは#[derive(XXX)]
アトリビュートによって自動的に実装できる(自動導出)。
例えば、Clone
、Copy
、Debug
、Default
、Eq
、Hash
、Ord
、PartialEq
、PartialOrd
は
自動導出できる。
第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
を置くと、ビルドスクリプトして認識される。