書籍概要

コンセプトから理解するRust

著者
発売日
更新日

概要

Rustはメモリ安全,スレッド安全を保ちつつ,高パフォーマンスなプログラムを開発できるプログラミング言語です。また,手続き型,オブジェクト指向型,関数型でのプログラミングに対応できるマルチパラダイムの言語でもあります。ただ,そういったRustのポテンシャルを引き出すには,所有権やライフタイム,ジェネリクスやトレイトといった特徴的な仕様の理解が求められ,これらは初学者の壁にもなっています。本書ではそれら難解な仕様をピックアップし,他のプログラミング言語とコードレベルで比較しながら,「なぜそのような仕様になっているか」という言語のコンセプトからRustの理解を試みます。加えて,Rustのこまやかなエラーメッセージを読みつつ,Rustをうまく書くための知識もお伝えします。

こんな方におすすめ

  • 一歩進んだプログラミングを学びたい,高パフォーマンスなシステムを作りたいITエンジニア

目次

第1章 Rustを学ぶにあたって

1.1 Rustの特徴

  • パフォーマンスと信頼性
  • 生産性
  • そのほかの特徴

1.2 Rustの効率的な学習法

1.3 Rustの情報源・利用可能なリソース

  • The Rust Programming Language(The book)
  • Rust By Example
  • 標準ライブラリのドキュメント
  • Webサイト「crates.io」
  • The Rust Reference
  • Rust Playground

第2章 Rustをはじめよう

2.1 Rustの開発環境のインストール

2.2 Rustでコードを書いてみよう

  • cargo newによるパッケージディレクトリの作成
  • 自動作成されるサンプルプログラムのコンパイル・実行
  • 「最初のコード」とその実行
  • Rustの記法・特徴的な書き方

2.3 Rustのコンパイルエラーメッセージ

2.4 テストの実装

2.5 クレートとモジュール

  • バイナリクレートとライブラリクレート
  • ソースコードの分割とモジュール
  • crates.io

2.6 高機能エディタVisual Studio Codeの導入

  • RustのコーディングをVS Codeで行うには
  • Rustでの開発に便利な拡張機能
  • clippyを使う

第3章 所有権とライフタイム

3.1 Rustにおける変数への値の割り当て

  • イミュータブルとミュータブル
  • 型推論

3.2 「束縛」の描像から見た,変数への値の割り当て

  • C言語における変数・値とメモリ
  • Pythonにおける変数と値の結び付け

3.3 Rustにおける変数の束縛と所有権

  • コピーセマンティクス
  • ムーブセマンティクス
  • 関数の引数への引き渡しによる所有権の移動,コピー

3.4 リファレンス

  • リファレンスによる所有権の借用
  • リファレンスのスコープ
  • ライフタイムとリファレンス
  • 関数定義におけるライフタイムパラメータ
  • リファレンスのルールのまとめ
  • 関数の引数を値にするか,リファレンスにするか

3.5 変数の再宣言とシャドーイング

3.6 所有権とメモリ安全性

  • メモリ管理の難しさ
  • Rustのメモリ管理

第4章 Rustにおける値の型

4.1 静的型付け言語と動的型付け言語

4.2 プリミティブ型

4.3 整数型・浮動小数点型

  • 型が異なる2つの整数の間の四則演算はできるか?
  • 整数の除算について

4.4 配列・タプル・スライス・ベクター

  • 配列
  • タプル
  • スライス
  • ベクター型

4.5 文字・文字列の型

  • 文字型
  • 文字列型
  • String型

4.6 構造体

  • 構造体の宣言
  • 構造体のインスタンスの作成
  • 構造体のフィールドにアクセスするときの自動的なデリファレンス
  • フィールド名と同じ変数がスコープの中にある場合の構造体インスタンスの作成
  • 同じ型の既存の構造体の一部フィールドの書き換えによる構造体インスタンスの作成
  • タプル構造体
  • Unit-Like構造体
  • 構造体へのメソッドの実装
  • 構造体,フィールド,メソッドの隠蔽
  • Debugトレイトの実装
  • Copyトレイトの実装
  • 構造体のフィールドにリファレンスを使う場合

4.7 列挙型

  • 単純な列挙型
  • 列挙型におけるmatchの活用
  • 列挙子にデータを持たせた列挙型

4.8 Option<T>型

  • Option<T>型の必要性
  • Option<T>型の定義
  • Option<T>型の値の生成
  • Option<T>型の値の処理

4.9 エラーハンドリングとResult<T,EV>型

  • エラーハンドリングのパターン
  • RustにおけるResult<T,EV>型によるエラーハンドリング

4.10 match式とif let

  • match式
  • if let

4.11 そのほかの便利な型

  • HashMap<K, V>
  • JSONを扱うserdeのValue型

4.12 メモリへの値の配置

  • 生ポインタ
  • メモリの領域
  • Vec型のデータはどこに置かれるか?
  • Box<T>型
  • constとstatic

4.13 Rc<T>型・RefCell<T>型・Weak<T>型

  • 関数内の自動変数は関数の外では使えない
  • C言語で関数の外で値を共有するにはヒープ領域に配置する
  • Rc<T>型
  • Rc<T>型を使って共通の値を所有権付きで参照する
  • RefCell<T>型
  • Rc<T>型の循環参照問題
  • Weak<T>型によるノード間の弱結合

第5章 Rustの抽象化プログラミング

5.1 Rustにおける抽象化

5.2 ジェネリクス

5.3 ジェネリクスの型に対するトレイトによる制約

  • トレイトの定義と実装
  • トレイトによる型パラメータへの制約(トレイト境界)
  • トレイトによる動作の追加実装と型の分類
  • メソッドのデフォルト実装
  • C++の型テンプレートとの比較

5.4 既存のトレイトの新しい型への実装

5.5 トレイトの関連型

  • トレイトの関連型の必要性
  • トレイトのデフォルト実装の活用

5.6 クラスによる抽象化との比較

  • 継承を使ったC++のコード例
  • トレイトを活用したRustでの実装
  • 新しい動作をC++のコードに追加する
  • 新しい動作をRustのコードに追加する

5.7 トレイトオブジェクト

  • 実行時に決まるエラーの型
  • 異なる型を要素とするVec<T>型

5.8 抽象返却値型

  • トレイトオブジェクトによる抽象返却値型
  • impl Traitによる抽象返却値型

5.9 静的・動的ディスパッチとゼロコスト抽象化

5.10 トレイトに関するトピックス

  • println!
  • Derefトレイト
  • デリファレンス型強制

5.11 トレイトに関する情報源

第6章 ファイルやソケットの入出力

6.1 ファイルの入出力

  • ファイルの内容を固定長のバッファに繰り返し読み出す
  • BufReaderを使って1行ずつ読み出す
  • モジュール化
  • バイナリファイルを読み出す
  • ファイルの書き込み
  • ファイルを開く際のオプションの指定

6.2 ソケットの入出力

第7章 Rustの関数型プログラミング向けの機能

7.1 関数型プログラミングとは?

7.2 イテレータ

  • レンジ
  • IntoIteratorトレイト
  • Vec<T>型に対するイテレータ
  • iter()とiter_mut()
  • Rustでのforループ
  • collect()

7.3 再帰関数

7.4 関数を引数とする関数

  • map()
  • filter(),fold(),reduce()

7.5 関数を返却する関数

  • 所有権が最初の呼び出しで消費されてしまうクロージャ(FnOnceの利用)
  • クロージャに閉じ込められた環境の変数を変更する場合(FnMutの利用)

7.6 パターンマッチの活用

第8章 Rustによるスレッド・非同期プログラミング

8.1 スレッドによる並列実行と非同期処理による同時実行

8.2 スレッドによる並列実行

  • スレッドによる並列実行を活用したベクターの要素の和の計算
  • スレッド間での共有データを参照するには
  • スレッド間での共有データに書き込みをするには
  • メインスレッドとの通信
  • ソケットの入出力を使ったエコーサーバ

8.3 非同期処理による同時実行

  • JavaScriptにおける非同期関数
  • Rustにおける非同期処理
  • 非同期タスクのランタイムによる実行
  • 非同期関数の同時実行
  • 非同期関数を用いたエコーサーバ

第9章 C言語のライブラリのRustからの利用

9.1 C言語で書かれたzlibの関数のRustからの呼び出し

9.2 呼び出す外部関数の引数が構造体である場合

9.3 自作のC言語のコードをRustのコードとリンクする

サポート

正誤表

本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。

(2024年10月28日最終更新)

P.183 図4.12 「カウンタ:3」部分

child: Node
child: None

(以下2024年5月30日更新)

P.320 リスト8.7

最後の行に以下が必要でした。

print_sum_hello(10000000)

(以下2024年4月1日更新)

P.190 図4.13中のテキスト(2ヵ所)

chaild
child

P.191 図4.14中のテキスト(2ヵ所)

chaild
child

P.193 図4.15中のテキスト(2ヵ所)

chaild
child

P.190 図4.16中のテキスト(2ヵ所)

chaild
child

P.214 下から8行目

また、EqおよびOrdは比較が必ずできる(失敗しない)型に対して、PartialEqおよびPartialOrdは比較ができない場合がある型に対して実装します。i32型などの整数型には、前者の"Partial"なしのトレイトが実装されているのに対し、f64、f32などの浮動小数点型には後者の"Partial" 付きのトレイトが実装されています。浮動小数点型の場合にはNaN(Not A Number)との比較ができないため、"Partial"付きになっています。
また、同じ型の同じ値同士の等値比較において、EqやOrdはいつでも真となることを前提にできる型に実装できるのに対し、PartialEqおよびPartialOrdはそれを前提にできる型に加え、できない型にも実装できます(数学的な言葉でいえば、PartialEqやPartialOrdは「その型に属する値が反射律を満たすことを要求されない型」に対して実装できるものです)。EqやOrdが実装される型には、自動的にPartialEqやPartialOrdも実装されています(EqやOrdがPartialEqやPartialOrdのサブトレイト(p216参照)であるため)。たとえば、浮動小数点型f32、f64はNaN(Not A Number)という値を含んでいますが、NaN == NaNの等値比較の結果は偽となるため、これらの型には"Partial"付きのトレイトのみが実装されています。一方、i32型などの整数型には、"Partial"なし、および"Partial"付きの両方のトレイトが実装されています。

(以下2023年7月31日更新)

P.57 上から4行目

=で結び付けたときの挙動はC言語と同じように考えることができます。
=で結び付けたときの挙動は次のように確認することができます。

P.57 下から3行目

すなわち、xやyというラベルが貼られた値は異なるメモリ領域に確保されており、xに何らかの操作をしてもxというラベルが貼られたメモリ領域の値が変わるだけで、yというラベルが貼られたメモリ領域の値はなんら影響を受けません(図3.8)。
すなわち、x、yのラベルが貼られている同一のイミュータブルな変数の値があるときに、x = x + 1のような操作をした場合、その計算結果を格納する新しいメモリ領域が確保され、その新しいメモリ領域のラベルがxになります。このとき、yというラベルが貼られた元のメモリ領域の値はなんら影響を受けません(図3.8)。

P.58 図3.8

中央の図の内容に誤りがありました。以下が正しい図です。

rust_image_p57_old.png

rust_image_p57_new.png

(以下2023年1月11日更新)

P.192 下から4行目

Rc::clone()
Rc::new()

(以下2022年12月26日更新)

P.23 3行目

std::fmt::Displayいうトレイト
std::fmt::Displayいうトレイト

P.71 3つ目のコード囲み10行目

myclear(&s);
myclear(s_ref);

P.142 リスト4.14のキャプション

Result<T,E>}
Result<T,E>

余計な「}」がありました。

P.154 Pythonのコード 10行目

print("The capital of {} is {}.".format(
print("The capital of {} is {}".format(

余計な「.」がありました。

P.154 Pythonのコード 14行目

print(The capital of {} is not found.".format(tg))
print("The capital of {} is not found".format(tg))

「"」が抜けており,余計な「.」がありました。

P.151 本文5行目

Some(“medium) を返却
Some(“large") を返却

P.154 Pythonのコード 11行目

tf, capitals["Japan"]
tg, capitals[tg]

P.171 リスト4.19 29行目

"to b3 12, 12: {}, {}",
"to be 12, 12: {}, {}",

P.171 エラー画面 1行目

rror[E0597]:
error[E0597]:

「e」が抜けていました。

P.174 エラー画面 2行目

to b3 12, 12: 12, 12
to be 12, 12: 12, 12

P.180 リスト4.22 31行目

"to b3 12, 12: {}, {}",
"to be 12, 12: {}, {}",

P.184 本文7行目

変換するのもので
変換するもので

「の」が不要です。

P.213 本文14行目

+演算子を使って表示
+演算子を使って表現

P.218 リスト5.11 32,41行目

// distance_eqの大小を、
// distance_sqの大小を、

(以下2022年12月1日更新)

P.17 端末画面

$ cargp clippy # clippyを実行
$ cargo clippy # clippyを実行

(以下2022年11月24日更新)

P.154 Pythonのコード 2行目

capticals["Japan"] = "Tokyo"
capitals["Japan"] = "Tokyo"

(以下2022年9月15日更新)

P.34 1行目

クレート名(json_serde)に変えるだけです。
クレート名(serde_json)に変えるだけです。

(以下2022年7月19日更新)

P.171 リスト 4.19の10行目

let number = Box::new(value + 10);
let number = value + 10;

(以下2022年6月1日更新)

P.245 本文9行目

実はこれは20行目と同じ意味になります。
実はこれは21行目と同じ意味になります。

(以下2022年3月30日更新)

P.289 3行目の漸化式

誤

正

最初のSの下付きは「N-1」ではなく「-1」であるべきでした。

商品一覧