Tour of Rust 3章を勉強した [学習記録]
Rustを学習できるwebアプリ、Tour of Rustをつかって最近勉強しています
すこしづつやっていまして、今日で3章終わりまでいけました!
ということで、簡単な学習記録をつけようと思います
Rustを一緒に勉強している皆さんの役にたてればと思います
※この記事は、「Tour of Rust 第3章終わったから自分でコード書いてみた」的な内容になっています。Tour of Rustの練習問題は出てきません
0. Tour of Rustって?
Tour of Rust とは、プログラミング言語 Rust の機能を段階的に学べる対話的なチュートリアルウェブサイトです
この記事は、Tour of Rust 3章最後、「データを持つ列挙型」のセクションの内容を扱います
コードはコーヒー屋さん
Tour of Rust 3章はenumとstructを解説する章です
せっかくなので、それらを使ったコーヒー屋さんのようなコード(?)を作りました
店員さんに渡す、注文用紙(伝票)を作るイメージです
「想定されるオーダー」をずべて受けきれるものでなければなりません
matchを使った条件分岐で、全ての可能性を網羅するようにします
コードの全体像
まずは完成したコードから。
※練習用のため使っていないコードがあることに注意してください
#![allow(dead_code)] // この行でコンパイラのwaringsメッセージを止めます
enum MySize { Short, Medium, Large, Order(u32) } // order=height[cm]
enum CType { Espresso, Cappuccino, American, Order(String) }
enum Sugar { Little, Lots, None, Order(u32) } // order=角砂糖の個数
enum Chara { Pikachu, Achamo, Mikalge, None }
enum Color { Green, Blue, Red, None }
struct Trend {
chara: Chara,
color: Color,
}
struct Coffee {
mysize: MySize,
ctype: CType,
sugar: Sugar,
trend: Trend
}
fn main() {
let my_coffee = Coffee {
mysize: MySize::Short,
ctype: CType::Cappuccino,
sugar: Sugar::None,
trend: Trend { chara: Chara::Pikachu, color: Color::Blue }
};
let yourcoffee = Coffee {
mysize: MySize::Order(20),
ctype: CType::Order("cafelate".to_string()),
sugar: Sugar::Order(4),
trend: Trend { chara: Chara::Achamo, color: Color::Red }
};
let size_string: String = match my_coffee.mysize {
MySize::Short => "ショートサイズ".to_string(),
MySize::Medium => "ミディアムサイズ".to_string(),
MySize::Large => "ラージサイズ".to_string(),
MySize::Order(height) => format!("オーダーサイズ {}", height)
};
println!("{} is selected!", size_string);
}
キーワードの紹介です
enumは選択肢を列挙します。structは、いわゆるオブジェクトで、キーと値が入ります。各項目はフィールドというのだとか
どちらもデータを受け取る準備をしているのであって、まだ実データは入ってきません。箱を作る作業をします
コードの解説(前半)
まずはmain関数の外、グローバル領域の解説をします
流れとしては、最初にenumで選択肢を定義し、次のstructで構造体、つまり注文をいれる箱を作るかたちです
enumについて
enumの文法は、enum MySize { Short, Medium, Large } だけではありません
enumの各要素はバリアントと呼ばれますが、3つの書き方が存在しています
enumの要素、3つの書き方
Rustでは、enumの各要素、つまり選択肢のことをバリアントと呼びます
ここには、普通に選択肢を列挙できるし、enumやstructをさらにネストして埋め込むようなことだって可能です
structは、JavaScriptでいうオブジェクトですね
完全にイコールではないけど、いろんな言語のオブジェクトが、Rustではstructに相当するらしいです
そして結論からいってしまいますが、enumの選択肢、バリアントには以下3つの種類があって、
- 普通に列挙
- タプル埋め込み
- struct埋め込み
こんなふうにガラパゴス的にまぜて使うことが可能です↓
enum {
Choice1, // 普通に列挙
Choice(u32), // タプル埋め込み
StructChoice {key: value, key2: value2} //struct(オブジェクト)埋め込み
}
※「タプル埋め込み=enum埋め込み」というわけではないみたい。難しい。
詳しく見ていきましょう。↓
1. ユニットバリアント(データを持たない。普通の選択肢)
enum MySize { Short, Medium }
// ↑ ↑
// バリアント1 バリアント2
2. タプルバリアント(名前なしのデータをもつ。タプル埋め込み)
enum MySize { order1(u32), order2(u32) }
// 名前(キー)を持たず、Valueだけ
3. 構造体バリアント(名前付きのデータを持つ。struct埋め込み)
enum MySize {
Custom { width: u32, height: u32 }
}
// "Custom:"としない。enumの唯一のセパレータは","でないとエラーがでる
「えっ、structとかenumって単語を中につけなくていいの?」と思うかもしれません
私も思いました
でも()や{}で自明だから、書かなくてもコンパイラーに伝わるらしいんです。
すごいですよね
// 間違ったコード
// こうenum enumしなくてい
enum Sample {
Choice1,
enum Choice(u32),
struct {...}
}
structの解説
struct Trend {
chara: Chara,
color: Color,
}
struct Coffee {
mysize: MySize,
ctype: CType,
sugar: Sugar,
trend: Trend
}
まずは2つ目のstructからお話します。こっちが本丸。これが注文用紙です
キャラと色、これらは「選択肢」ではなく「全部使うデータ」なのでstructで定義しました
main処理 - 実データ作成
構造体はあくまで「注文用紙」
それ自体はなんのオーダー情報も入っていません
なので注文を取るときは構造体を使って、実際の注文データを構築します
実際にデータを作る1
let my_Coffee = Coffee {
mysize: MySize::Short,
ctype: CType::Cappuccino,
sugar: Sugar::None,
trend: Trend { chara: Chara::Pikachu, color: Color::Blue }
};
enumで定義された選択肢を選択をする場合は、名前::選択肢のようにします
mysize: Mysize::Short
こうすることで、実データに "Short" という選択肢がのります
ただ、実際に "Short" という文字列が登録されるわけではないので注意が必要です
最後ややこしくなっている trend の部分は、trendのキーに構造体を渡しています
実際にデータを作る2
let your_Coffee = Coffee {
mysize: MySize::Order(20),
ctype: CType::Order("cafelate".to_string()),
sugar: Sugar::Order(4),
trend: Trend { chara: Chara::Achamo, color: Color::Red }
};
ユニットバリアント、つまりデータを持たない普通の選択肢はMySize::Shortとすれば大丈夫でした
では名前無しデータを持つタプルバリアントはどうするのでしょうか?
その場合は、mysize: MySize::Order(20)このように()をつけて数値や文字列を渡します
条件分岐と出力
最後の節までやってきました。
本当は入れた内容全てに対し条件分岐をしたいとこですが、練習用のため一部のみ実装してあります
matchを使えば、簡単に条件分岐をかけます。
個人的にはif, elseifより好きかも。
でも複雑な真偽判定はどうやるんでしょうか?←また今度の課題です
let size_string: String = match myCoffee.mysize {
MySize::Short => "ショートサイズ".to_string(),
MySize::Medium => "ミディアムサイズ".to_string(),
MySize::Large => "ラージサイズ".to_string(),
MySize::Order(height) => format!("オーダーサイズ {}", height)
};
println!("{} is selected!", sizeString);
ここでは、matchでsize_stringに入る文字列を条件分けし、それを出力しています
ここでは、以下のことがわかりました
- matchでは各条件の戻り値は全て同じ型でなければならない
-
format!()は、「文字列にフォーマット」してくれる - MySize::Shortは、Shortという文字列を返すわけではない
最初、let size_string: &str = ... としていました
"ショートサイズ"は&str(文字列スライス)なのでOK
しかし、最後の format! だけStringで帰ってくるのでエラーになってしまいます。
(format!("オーダーサイズ {}".to_string(), height)としなくてもいいらしい。)
また、MySize::ShortやMySize::Mediumは0,1といったインデックス?に変換、コンパイルされるらしく、let size: String = MySize::Medium としたらエラーになります
インデックス?で返すのか文字列で返すのかわからないかららしい
まとめ
今回はTour of Rust 3章のまとめとして学習記録を書いてみました。
この3章最後難しいですよね。
何日も止まっていたけど、頑張って最後までやりました!
そういえば、ここでよく登場するferrisという単語、カニのrustのマスコットキャラクターだそうですね!とてもかわいいです!
Discussion