The Rust Book 輪講 6-7 章
今回のテーマ
6. Enumとパターンマッチング
7. 肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する
便利リンク
/emoji/rust.icon/emoji/rust.icon/emoji/rust.icon
6. Enumとパターンマッチング
6.1. Enumを定義する
列挙型(直和)
構造体(直積)とは双対の概念
書き方 enum 型名 { 列挙子, ..., 列挙子 }
値の参照: 型名::列挙子
列挙子 variant
備忘)変数は variable
table:呼び方
型 値(構成要素)
構造体 struct フィールド field
列挙型 enum 列挙子 variant
列挙型の中で構造体を使う(宣言は enum だけでOK)
maton.icon 積の和! 気持ち: (u8×u8×u8×u8)+Strting
別途宣言した構造体を列挙子に持たせても良い
列挙子に匿名構造体を持たせても良い→例を見よ
列挙型へのメソッド実装も可
便利な列挙型:Option(<T> はジェネリクス、いろんなデータ型が入ることを認める→10章)
code::rust
enum Option<T> {
Some(T),
None,
}
Rust では null 参照を認めない→値がない可能性があるデータを表したければOptionを使え!
トニー・ホーア「null参照を作ったばかりに10億ドルの苦痛と損害を出した、ほんまごめん」
何が嬉しいの?
Optionのデータが「ある」場合と「ない」場合の対処をコンパイラが強制する
うわっ知らん間にnullになっとった!(実行時に気づく)というのを防ぐ
maton.icon unwrap() を使うとNoneの場合に実行時エラーが起きるので、ご利用は計画的に
Noneのときにデフォルト値入れたいんだけど!→ unwrap_or(def: T)
Noneのときは致命的なエラーってことにしたいんだけど!→ expect(msg: &str)
Noneはいいとして、Someの時に追加で計算したいんだけど!→ map(func: 関数)
ていうか場合分けしたいんだけど!→match使え(後述)
6.2. matchフロー制御演算子
列挙型はmatchで分解できる(場合分け)
Java でいう switch みたいなもん
maton.icon match で分解するときも 型::列挙子 という書き方が要求される?そこは型を省略できるようにしてほしいかも
試してみると、エラーにはならないが警告は出る模様
関数の中で use Coin::*; を叩いて各列挙子をスコープに入れてしまえば型を省略できる(Optionとかはそうなってるはず)
入れ子(データ付き列挙子)
Someの中身までマッチできる(i に束縛されてる)
もっと言えば、列挙子の中の構造体や、列挙子の中の列挙しみたいな深い入れ子でもマッチできる
match では列挙型の全列挙子を網羅しないとコンパイルエラー、MECEに書け😤
error[E0004]: non-exhaustive patterns: Hoge not covered
そうは言っても一部にしか興味がない場合もある
「その他の場合」に使えるプレースホルダー _ => hoge() 使え
6.3. if letで簡潔なフロー制御
列挙型の中身を分解したいけど、その中の1つにしか興味がない場合ってあるよね?
if let 記法が便利(matchの糖衣構文的な)
パターンマッチに成功すれば、if のスコープ内へ、そうでなければ if の外へ
else 節も書ける→match 式におけるプレースホルダー _ と同じ
if-else のはしごにしても良い
maton.icon なんか閉じた集合で表せそうな業務概念があるなと思ったら列挙型をじゃんじゃん使うのが良さそう
/emoji/rust.icon/emoji/rust.icon/emoji/rust.icon
7. 肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する
用語の整理😫
パッケージ: クレートをビルドし、テストし、共有することができるCargoの機能
クレート: ライブラリか実行可能ファイルの生成する、木構造をしたモジュール群
モジュール と use: これを使うことで、パスの構成、スコープ、公開するか否かを決定できます
パス: 要素(例えば構造体や関数やモジュール)に名前をつける方法
(ワークスペース: 同時に成長するパッケージの集まり→14章)
7.1. パッケージとクレート
パッケージ→Cargo.toml にビルド・テスト・共有方法を定義https://gyazo.com/e5f88eec638c7d1e4f02ab2c2980b00b
パッケージは1つ以上のクレートを持つ(バイナリかライブラリ)↑
ライブラリは高々1つ、バイナリは何個でもOK(複数の場合は src/bin にフォルダを掘って置く)
src/main.rs と src/lib.rs はクレートルート→Cargo.toml に何も書かなくても認識される
7.2. モジュールを定義して、スコープとプライバシーを制御する
mod キーワードによるモジュールの定義
入れ子で宣言→木構造となる
7.3. モジュールツリーの要素を示すためのパス
モジュールをどうやって指定する?→パスを使う
絶対パス
クレート名および crate から指定
相対パス
self, super, あるいはモジュール内の識別子から指定
モジュールの公開設定(プライバシー境界)
あらゆる要素はデフォルトで非公開(モジュール外からアクセスできない(ただし兄弟要素からはアクセス可))
pub mod によってモジュール内にアクセス可能に(ただし各識別子はまだ非公開)
pub fn, pub struct などによってモジュール外からアクセス可能になる
公開可能:関数、メソッド、構造体、enum、モジュール、定数
impl をそのまま pub にはできないけど、impl の各メソッドに対して pub はつけられる
ややこしいケース
pub struct では、フィールドはデフォルトで非公開
pub enum では、列挙子はデフォルトで公開(普通は列挙子の方に用事があるので)
7.4. useキーワードでパスをスコープに持ち込む
モジュールのパスをいちいち書くのはだるい→ use してパスをスコープに持ち込めば短く書ける
プライバシー境界自体は変化しない
モジュールをスコープに持ち込むことも、モジュール内の関数を持ち込むこともできる
慣例
関数→親のモジュールまで use して、モジュール名::関数名 で呼び出す
どこで定義された関数かがわかりやすい
構造体→構造体を use して、構造体::関連関数 で呼び出す
同じ名前の構造体を使い分ける→親モジュールまで use して、モジュール名::構造体 で呼び出す
ちょっとだるい→ as キーワードで別名を与える
code::rust
use std::fmt::Result;
use std::io::Result as IoResult;
名前の再公開 re-expoting
pub use crate::hoge::huga
ネストしたモジュールから代表的な機能を集めてきて、使いやすい名前空間に配置する、という使い方ができる
外部のパッケージを使う
Cargo.toml にパッケージを書き加える→クレートを use できるようになる
共通したパスの整理→共通部分をまとめて、差異がある部分を波括弧で表現
glob演算子 * →パスに存在するすべての公開要素をスコープに持ち込む
多様厳禁だが、テストやPreludeパターンで使われている
7.5. モジュールを複数のファイルに分割する
ディレクトリ構造+ファイル名がモジュール構造になっている
mod name; → モジュール読み込み
mod name {...} → モジュールの定義
src/front_of_house.rs に pub mod hosting {...} すると、 src/lib.rs から pub use crate::front_of_house::hosting; で呼べる
src/front_of_house/hosting.rs に pub fn add_to_waitlist() {} を宣言すると、この関数は crate::front_of_house::hosting::add_to_waitlistというパスを持つ
おもしろ例題:兄弟と子モジュールで同じ名前だったら
code:rust
fn main() {
eat_at_restaurant();
}
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("brother");
}
}
}
pub fn eat_at_restaurant() {
pub mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("child");
}
}
}
// Absolute path
// 絶対パス
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
// 相対パス
front_of_house::hosting::add_to_waitlist();
// 優先度は Child > Brother !!!
}