コース目次 / 第4章

自分のものにする

enum で軽減税率を足して、配れる実行ファイルに固めます。

第4章 / 全5章目安 約45分この章のゴール: 税込み合計が出て、cargo build --release で配れる1個のファイルができる
お使いのエディタ
ファイル操作が切り替わります

最後の章です。enum を1つ足して、それがどれだけ心強いかを体験します。

1. 区分を足す

食品は軽減税率で8%、それ以外は10%。そういう「種類」を表すのが enum です。

src/main.rs+15 / -1
 use std::io::{self, Write}; +enum Kubun {+    Shokuhin,+    Sonota,+}+ struct Mono {     namae: String,     nedan: u32,+    kubun: Kubun, } +// 区分ごとの税率。ここに区分を1つ足すと、コンパイラが直す場所を全部教えてくれる。+fn zeiritsu(kubun: &Kubun) -> f64 {+    match kubun {+        Kubun::Shokuhin => 0.08,+        Kubun::Sonota => 0.10,+    }+}+ fn main() {     let mut memo: Vec<Mono> = Vec::new();      println!("買い物メモ");-    println!("「名前 値段」で追加、list で一覧、quit で終わり。");+    println!("「名前 値段」で追加(食品なら「名前 値段 f」)、list で一覧、quit で終わり。");      loop {

行頭の + - は変更の目印です。コピーすると、記号と削除された行を除いたこの章を終えた時点の内容が入ります。

src/main.rs+8 / -2
     let bubun: Vec<&str> = gyou.split_whitespace().collect(); -    if bubun.len() != 2 {-        println!("「名前 値段」のように、2つに分けて書いてください。");+    if bubun.len() < 2 || bubun.len() > 3 {+        println!("「名前 値段」、食品なら「名前 値段 f」と書いてください。");         return;     } +    let kubun = if bubun.len() == 3 && bubun[2] == "f" {+        Kubun::Shokuhin+    } else {+        Kubun::Sonota+    };+     // parse は「うまくいったかどうか」ごと返してくる。両方の場合を書かないと通らない。     match bubun[1].parse::<u32>() {

行頭の + - は変更の目印です。コピーすると、記号と削除された行を除いたこの章を終えた時点の内容が入ります。

src/main.rs+1 / -0
                 namae: bubun[0].to_string(),                 nedan,+                kubun,             });             println!("{} を入れました。", bubun[0]);

行頭の + - は変更の目印です。コピーすると、記号と削除された行を除いたこの章を終えた時点の内容が入ります。

src/main.rs+12 / -4
     } -    let mut goukei = 0;+    let mut zeinuki = 0;+    let mut zeikomi = 0.0;      for (i, mono) in memo.iter().enumerate() {-        println!("{}. {} {}円", i + 1, mono.namae, mono.nedan);-        goukei += mono.nedan;+        let shirushi = match mono.kubun {+            Kubun::Shokuhin => "食",+            Kubun::Sonota => "  ",+        };++        println!("{}. {} {} {}円", i + 1, shirushi, mono.namae, mono.nedan);++        zeinuki += mono.nedan;+        zeikomi += mono.nedan as f64 * (1.0 + zeiritsu(&mono.kubun));     } -    println!("合計 {}円", goukei);+    println!("合計 {}円(税込 {}円)", zeinuki, zeikomi.round() as u32); }

行頭の + - は変更の目印です。コピーすると、記号と削除された行を除いたこの章を終えた時点の内容が入ります。

保存して cargo run。

やってみる
> りんご 180 f
> パン 250 f
> 洗剤 400
> list
こう出る
1. 食 りんご 180円
2. 食 パン 250円
3.    洗剤 400円
合計 830円(税込 904円)

2. ここがこのコースの山場

enum Kubun に、区分をもう1つ足してみてください。

src/main.rs(1行足すだけ)
enum Kubun {
    Shokuhin,
    Nomimono,
    Sonota,
}

保存して cargo run。2つエラーが出ます。

コンパイラの言い分(抜粋)
error[E0004]: non-exhaustive patterns: `&Kubun::Nomimono` not covered
  --> src/main.rs:17:11
   |
17 |     match kubun {
   |           ^^^^^ pattern `&Kubun::Nomimono` not covered

error[E0004]: non-exhaustive patterns: `Kubun::Nomimono` not covered
  --> src/main.rs:89:30
   |
89 |         let shirushi = match mono.kubun {
   |                              ^^^^^^^^^^ pattern `Kubun::Nomimono` not covered

これがRustを使う最大の見返りです。「種類を1つ増やしたら、直すべき場所はこの2か所」と、漏れなく、行番号つきで教えてくれました。
他の多くの言語では、直し忘れた場所が本番で初めて見つかります。Rustではそもそもコンパイルが通りません。
コース2で「赤い波線は出発前に気づけた忘れ物」と書きました。Rustはそれを、もっと強く、もっと執拗にやってくれる言語です。

言われた2か所に Kubun::Nomimono の枝を足せば通ります。元に戻して先へ進んでも構いません。

3. 配れる1個のファイルにする

VS Codeのターミナルターミナル
cargo build --release

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

こう出る
    Finished `release` profile [optimized] target(s) in 0.65s

target/release/kaimono(Windowsは kaimono.exe)ができています。この教材を書いた環境では439KBでした。

VS Codeのターミナルターミナル
./target/release/kaimono

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

このファイルには、Rust本体ごと必要なものが入っています。だからRustが入っていないパソコンでも、同じOSなら動きます。友だちに送っても、USBに入れても動きます。
–release を付けないビルドは、速く組み立てるかわりに遅く動きます。作っている間は cargo run、配るときは –release と覚えてください。

あなたの番

3つとも、値を書き換えるだけです。

(1) 税率を自分の国・自分の年に合わせる

zeiritsu の中の数字を書き換えてください。1か所を直せば、合計の計算が全部ついてきます。

(2) 区分を増やす

さっきの Nomimono を本当に足してみてください。コンパイラが道案内をしてくれるので、迷いません。

(3) 表示を変える

"食" の印を絵文字にしたり、合計の行に「あと○円で送料無料」を足したり。println! の中を変えるだけです。

持ち帰りカード

フォルダに README.md を置いておきます。3か月後のあなたが読む用です。

README.md
# はるとの買い物メモ

Tsumiki のミニ体験コース「Rust — コンパイラと二人三脚で書く」の成果物です。

## また動かすとき(3行)

1. ターミナルを開く
2. `cd` でこのフォルダに入る
3. `cargo run` と打つ

配れる1個のファイルにするときは `cargo build --release` です。できあがりは
`target/release/kaimono`(Windowsは `kaimono.exe`)。**Rust が入っていない
パソコンでも、同じOSなら動きます。**

## 使い方

| 打つもの | 何が起きるか |
|---|---|
| `りんご 180` | その他(税10%)として追加 |
| `りんご 180 f` | 食品(軽減税率8%)として追加 |
| `list` | 一覧と、税抜き・税込みの合計 |
| `quit` | 終わり |

## 中身

| ファイル | 何をするか |
|---|---|
| `src/main.rs` | 本体。ここだけ書き換えれば自分用になる |
| `Cargo.toml` | このフォルダが1つのRustプロジェクトであるという印 |
| `Cargo.lock` | 使った部品の版を固定する。**消さずに一緒に持っておく** |
| `target/` | ビルドの置き場。**消しても `cargo build` で作り直せます**(数百MBあります) |

## コンパイラを味方にするコツ

- **赤いエラーは、直し方まで書いてあります。** `help:` の行を先に読む
- `enum Kubun` に区分を1つ足してみてください。**直すべき場所を、コンパイラが
  漏れなく列挙してくれます**。これがRustで型と `match` を使う最大の見返りです
- 意味が分からないエラー番号は `rustc --explain E0382` のように聞けます

## この先に進むなら

- The Rust Programming Language(日本語版) https://doc.rust-jp.rs/book-ja/
- Rust By Example(日本語版) https://doc.rust-jp.rs/rust-by-example-ja/
- このコースでは所有権を1回しか触っていません。次に進むなら、そこが本丸です

試食レポート

おつかれさまでした。手元には動く道具と、配れる実行ファイルが残りました。

Rustは「動かす前に、できるだけ多くの間違いを潰す」ことに全力を注いだ言語です。書くのに時間がかかるかわりに、通ったコードは驚くほど安定して動きます。

この言語、合いそうでしたか?
よく怒られること、そのかわり直し方まで教えてくれること、通ったときの安心感。合いそうなら、The Rust Programming Language(日本語版)が決定版です。次の山は所有権で、この試食では1回しか触っていません。
ぴんと来なかったなら、それも収穫です。Rustが向くのは「壊れては困るもの」で、書き捨ての道具には他の言語のほうが速く着きます。別の言語を試食してみてください。

ハマったら

ZIPで戻したい

このページの下のZIPは、この章を終えた状態のフォルダです。解凍して、その中で作業を続けられます。自分で書き換えた部分は消えてしまうので、先に自分のファイルを別の場所へ待避してから使ってください。

こうなっていればOK

うまくいかないときは、この章の完成ファイル一式(ZIP)をダウンロードして続きから進めても大丈夫です。

この章で卒業です。

この章はまだ完了していません。