Rustの基本

勉強の備忘録
開発環境は整っているものとします。
基本タメ口ですが、敬語を意識してそっちにリソースが割かれるのが嫌なので許してください。

https://doc.rust-jp.rs/book-ja/
ここと並行して進めていくつもりです。
（最初に開発環境は整っているものとしているので1.は省略）

# 2. 数当てゲームのプログラミング

### プロジェクトの立ち上げ

```bash
cargo new guessing_game
```

新規プロジェクトを作成すると、Hello worldプログラムが生成される

src/main.rs
```rs
fn main() {
    println!("Hello, world!");
}
```

### プロジェクトの実行
```bash
cargo run
```

### 予想を処理する

```rs
use std::io;

fn main() {
    println!("数当てゲーム");
    println!("予想を入力してください");
    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("行の読み込みに失敗しました");
    
    println!("右のように予想しました：{}", guess);
}
```

```rs
use std::io;
```

標準入出力を受け取るために、stdというライブラリ内のioライブラリをインポート

```rs
fn main(){
```
プロジェクトのエントリポイント
```cargo run```するとmain関数がエントリポイントになる（エントリポイントを変更する方法などは不明）

```rs
println!("数当てゲーム");
```

コンソールに文字列を表示
!マークがついているのはマクロという意味

```rs
let mut guess = String::new();
```
変数の定義は以下
```rs
let guess = String::new();     //イミュータブル
let mut guess = String::new(); //ミュータブル 
```

変数名の前にmutを入れなければミュータブルにならない（デフォルトではイミュータブル）

```rs
String::new();
```
String型のインスタンスを新たに作成している

- ユーザーの入力を受け取る
    ```rs
    io::stdin();
    ```
    ioライブラリのstdin(標準入力受け取り)を実行
    
    ```rs
    std::io::stdin();
    ```
    このようにすれば最上位のuse句がなくても動く

    ```rs
    .read_line(&mut guess)
    ```
    ここでユーザーからの入力を得ている
    ```&mut guess```ここであらかじめ定義したguessの中に受け取ったデータを入れている
    ```&```をつけることで参照（メモリアドレス）であることを表す
    また、参照自体もデフォルトではイミュータブルであるため、```&guess```ではなく```&mut guess```のようにしている

    ```rs
    .expect("行の読み込みに失敗しました");
    ```
    ioの中にはResult型というものがあり、処理が成功したか失敗したかがわかるらしい
    そのResult型の中にはexpectメソッドがあり、ここではそれを呼び出している

- println!マクロのプレースホルダーで値を表示する
    ```rs
    println!("右のように予想しました：{}", guess);
    ```
    ```{}```の部分にguessの値が入る
    ```rs
    let x = 5;
    let y = 10;

    println!("x = {} and y = {}", x, y);
    ```
    複数の場合はこう

### 乱数を生成する
- クレートを使用して機能を追加する
    Rustの標準ライブラリには乱数を作成するものはないので外部のクレートを使う
    ※ クレートとはpythonでいうpipみたいなもの？（認識が違ったら教えてください）
    Cargo.tomlファイルにrandクレートを追加
    - Cargo.toml
        ```toml
        [dependencies]
        rand = "0.8.3"
        ```
    - ビルド
        ```bash
        cargo build
        ```
        この状態でbuildするとクレートをダウンロードしてコンパイルしてくれる
        ```bash
            Blocking waiting for file lock on package cache
            Updating crates.io index
            Blocking waiting for file lock on package cache
            Blocking waiting for file lock on build directory
        Compiling cfg-if v1.0.0
        Compiling ppv-lite86 v0.2.17
        Compiling getrandom v0.2.8
        Compiling rand_core v0.6.4
        Compiling rand_chacha v0.3.1
        Compiling rand v0.8.5
        Compiling tutorial v0.1.0 (C:\git\rust-practice\tutorial)
            Finished dev [unoptimized + debuginfo] target(s) in 1m 44s
        ```
    - 乱数を生成する
        ```rs
        use std::io;
        use rand::Rng;

        fn main() {
            println!("数当てゲーム");
            let secret_number: u32 = rand::thread_rng().gen_range(1..101);
            println!("秘密の数字は{}です", secret_number);
            println!("予想を入力してください");
            let mut guess = String::new();

            io::stdin()
                .read_line(&mut guess)
                .expect("行の読み込みに失敗しました");
            
            println!("右のように予想しました：{}", guess);
        }
        ```
        use句でrandクレートをインポート
        ```rs
        let secret_number: u32 = rand::thread_rng().gen_range(1..101);
        ```
        この行で秘密の数字を乱数で作成
        ```rs
        1..101
        ```
        は範囲を表す
        この場合は1~100になる
        仮に```1..=101```とすると1~101の範囲になる
### 予想と秘密の数字を比較する
```rs
use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
    println!("数当てゲーム");
    let secret_number: u32 = rand::thread_rng().gen_range(1..101);
    println!("秘密の数字は{}です", secret_number);
    println!("予想を入力してください");
    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("行の読み込みに失敗しました");
    
    println!("右のように予想しました：{}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}
```

```rs
use std::cmp::Ordering;
```
という型をスコープに追加
Orderingはenumの一種でLess、Greater、Equalという列挙子を持っている

```rs
match guess.cmp(&secret_number) {
    Ordering::Less => println!("Too small!"),
    Ordering::Greater => println!("Too big!"),
    Ordering::Equal => println!("You win!"),
}
```
cmpメソッドは値を比較できるものならなんでも使える
guessとsecret_numberの参照で比較をとる
matchに関しては6章で詳しく解説するらしいのでここでは触れない

- 型の不一致の修正
    このままだとコンパイルができない
    ```match guess.cmp(&secret_number)``` で比較する際に、guessはStringなのに対し、secret_numberはu32型であるため、型の変更が必要

    ```rs
        .expect("行の読み込みに失敗しました");

    let guess: u32 = guess.trim().parse()
        .expect("Please type a number!");  
    ```
    この2行を追加する
    guessという変数名で、guessをu32型に変換したものを格納している
    これは「シャドーイング」といい、同じ変数名を再利用することができる
    ```rs
    guess.trim().parse()
    ```
    guessはこの時点ではString型である
    Stringオブジェクトのtrim関数を実行（先頭と末尾の空白を削除する）
    Stringオブジェクトのparseメソッドは文字列をなんらかの数値にする
    → 手前で```let guess: u32```と型を明示しているので、この場合は```parse```メソッドはu32型に変換すればいいと認識をしてくれる
    また、```parse```メソッドは```read_line```メソッド同様Result型を返すので、```.expect("Please type a number!");``` でエラー時のメッセージを書く

        
### 出力

```bash
cargo run
```

```bash
数当てゲーム
秘密の数字は7です
予想を入力してください
50
右のように予想しました：50
Too big!
```

### ループで複数の予想を可能にする

```rs
use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
    println!("数当てゲーム");
    let secret_number: u32 = rand::thread_rng().gen_range(1..101);
    println!("秘密の数字は{}です", secret_number);
    loop{
        println!("予想を入力してください");
        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("行の読み込みに失敗しました");
        
        let guess: u32 = guess.trim().parse()
            .expect("Please type a number!");  
        
        println!("右のように予想しました：{}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}
```

loopの中に予想して比較する部分を入れると無限にループするようになる

```rs
match guess.cmp(&secret_number) {
    Ordering::Less => println!("Too small!"),
    Ordering::Greater => println!("Too big!"),
    Ordering::Equal => {
        println!("You win!");
        break;
    }
}
```

Orderingの各値は```{}```で囲むと複数行の処理が記述できるらしい

### 不正な入力をキャッチする

```rs
use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
    println!("数当てゲーム");
    let secret_number: u32 = rand::thread_rng().gen_range(1..101);
    println!("秘密の数字は{}です", secret_number);
    loop{
        println!("予想を入力してください");
        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("行の読み込みに失敗しました");
        
        let guess: u32 = match guess.trim().parse(){
            Ok(num) => num,
            Err(_) => continue,
        };
        
        println!("右のように予想しました：{}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}
```

```rs
let guess: u32 = match guess.trim().parse(){
    Ok(num) => num,
    Err(_) => continue,
}
```

ここを書き換えた
先ほどparseメソッドはResult型を返すと書いたが、Result型はOkとErrの列挙子を持つ
- Okの場合は引数に入力した値が入っている
- Errの場合はどのエラーとマッチするかを```(_)```で表している
    - ```_```の場合はすべてをマッチマッチさせている
    - これで実質敵にparseメソッドが遭遇しうるエラーを回避することができる

## 最後に
とりあえずやってみたが、まず日本語ドキュメントがあることがびっくり
しかもそのドキュメントがかなり細かく書かれているので、習得が難しい言語と言われているが少し頑張ってみようと思う
まだまだ書き方は全然わからないのでその辺を今後とも勉強していきたい
ほんとにRustを触ったのは初めてなので、間違った認識があったらどんどん言ってもらえるとうれしいです。