The Rust Programming Language を徹夜で読んでやれるだけ試してみた

この記事は [Wano Group Advent Calendar 2019](https://qiita.com/advent-calendar/2019/wano-group) の14目の記事となります。

**先日の3月に**開発合宿に行ってきました。というわけで、早速レポートしたいと思います。

なんか、すみません。

※Perlのネタを書くつもりでしたが、そちらは[Perl Advent Calendar](https://qiita.com/advent-calendar/2019/perl) に書くことにします。

## 開発合宿

Wanoグループでは開発合宿として3月あたりに、エンジニアで集まって開発合宿に行っています。
詳しくは、[こちらを](https://group.wano.co.jp/blog/396/)参照してください。

僕は、社内でいつの間にかRustのコードができあがっていたので、それを読めるようならねばということで、一夜漬けでRustのドキュメントをひたらすら読んで試すということをしていました。

9ヶ月前にガーッとやったやつを、また、ザーッと見直して、ブログに落としているので、多分、色々間違っている可能性が否定できません...。

## 参考にしたドキュメント

タイトルの通り、[The Rust Programming Language](https://doc.rust-jp.rs/book/second-edition/foreword.html)を読み進めました。

## コマンドについて

* rustup doc ... ドキュメントが立ち上がる
* cargo doc --open ... 自分で作ったコードのドキュメントが立ち上がる
* rustc main.rs ... main というバイナリができあがります

## main

とりあえず、実行したいなら main 関数を作ります。
きっと、標準入出力も扱うでしょうから、`std::io`も `use`しましょう。

```rust
use std::io;

fn main() {

}
```

## 変数宣言

let で宣言できます。型を明示的に指定しなければ、型推論してくれます。

```rust

    let a = 1; // 型推論
    let b :i32 = 1; // 明示
```

この宣言では、変数は、immutable(不変)なので、変更できません。
一度宣言した変数に、別の値を割り当てることは出来ません。

変更したい場合は、 `mut` を付けます。

```rust
    let mut a = 1; // 変更可能な変数には mut を付ける
    a *= 10; // 変更できます
```

### 文字列

文字列は下記のようにします。両方同じ。

```rust
let s = String::from("abc");
let h = "へろー".to_string();
```

`chars` でイテレータが返ります。

```rust
    for c in s.chars() { // 文字単位
        println!("{}",c)
    }

    for b in s.bytes() { // バイト単位
        println!("{}",b)
    }
```

### 文字連結

`push_str` を使います。

```rust
    let mut s = String::from("はろー");
    s.push_str(", わーるど"); // 文字の連結
```

`+` も使えますが、その場合、右側に参照を渡します。

```rust
    let s2 = String::from("! あいうえお");
    let s = s + &s2; // + も使えるけど、右側には参照を渡す
```

### 文字列のスライス

```rust
    let v = s.chars().nth(0).expect("「は」が取れるはず"); // expect は、失敗した際に panicする時に出す文字列
    println!("{}", v);
```

オーバーすると、ぱにくります。

```rust
    let v = s.chars().nth(100).expect("ぱにくる");
```

### 文字列から数字

`unwrap()`というのを使ってますが、`parse`が Option型というのを返してくるためです(後述)。

```rust
    let num = "123".to_string().parse::<i32>().unwrap();

    let num2 = 123;

    if num == num2 {
        println!("同じ")
    }
```

## ループ

for は既に書きましたが、無限ループ。

```rust
    let mut cnt = 0;

    let n = loop { // 無限 loop
        cnt += 1;
        println!("ループ!: {}回目", cnt);
        if cnt == 10 {
            break cnt // break を loop の戻り値として返せる
        }
    };

    println!("breakの戻り!:{}", n);
```

## Option型/Result型

前述のparseのように失敗する可能性のあるものが、Result型とかOption型とかを返す場合があります。
下の場合、nth もサイズを超えると失敗するので、Option型を返してきます。

`match`を使って処理できます。

```rust
    let r = s.chars().nth(100); // Option を返してくる
    let v = match r {
        Some(_) => s, // なんか返ってきたら
        None => String::from("NULL"), // オーバーしたら"NULL"って返す
    };
```

## 所有権

Rustには、`所有権`という概念があります。
`pass_string`という関数があったとして、それに変数 `v`を渡した場合。

```rust
    let s = pass_string(v);
    println!("{}", s);

    println!("{}", v); // pass_string で moveされているので死ぬ
```
所有権が移ってしまったものは利用できません。

下記のように、関数に参照渡しをした場合は、死にません。

```rust
    let v = pass_ref_string(&s);
    println!("{}", s); // 参照渡しなので死なない
    println!("{}", v);
```

pass_string, pass_ref_string の定義は下記。

```rust
fn pass_string (s: String) -> String {
    s
}

fn pass_ref_string (s: &String) -> &String {
    &s
}
```

immutableなので、関数の中で変更できませんが、下記の　modify_string のような定義であれば変更できます。

```rust
fn modify_string (mut s: String) -> String {
    s.push_str("あああ");
    s
}
```

以下のように使います。

```rust
    let v = String::from("えええ");
    println!("{}", modify_string(v)); // modify_string 側で mutを付けると変更できる

    if s == "NULL" { // v は modify_string で moveされているので使えない
        println!("True");
    } else {
        println!("False");
    }
```

## enum

宣言。

```rust
enum Breakfast {
    Rice,
    Bread,
}
```

使い方。

```rust
    let rice = Breakfast::Rice;
    let bread = Breakfast::Bread;

    let breakfast = [rice, bread]; // 配列

    for r in breakfast.iter() {
        // enum をパターンマッチ
        match r {
            Breakfast::Rice => {
                println!("米がたべたい")
            },
            Breakfast::Bread => {
                println!("パンがたべたい")
            },
        }
    }
```

### 文字列を取る enum を使う

宣言。

```rust
// 引数を取れる Debug trait を利用
#[derive(Debug)]
enum Breakfast2 {
    Kind(String),
    Calorie(u32),
}
```

使い方

```rust
    let b = Breakfast2::Kind(String::from("炊き込みご飯"));
    let c = Breakfast2::Calorie(1000);

    // Breakfast2 は std::fmt::Display を実装していないので、"{}"は使えない
    // #[derive(Debug)] しているので、"{:?}" は使える
    println!("朝食の種類:{:?}", b);
    println!("カロリー:{:?}", c);

```

### 自分で、std::fmt::Display を実装する

```rust
enum Breakfast3 {
    Kind(String),
    Calorie(u32),
}
// 自分で、std::fmt::Display を実装
// 下記からパクったけど、IDE上で赤くなるけど、特に文法エラーではない
// https://users.rust-lang.org/t/printing-enum-values/21002
impl std::fmt::Display for Breakfast3 {
    fn fmt(&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Breakfast3::Kind(v) => v.fmt(f),
            Breakfast3::Calorie(v) => v.fmt(f),
        }
    }
}
```

使い方。

```rust
    // Breackfast3
    let b = Breakfast3::Kind(String::from("炊き込みご飯"));
    let c = Breakfast3::Calorie(1000);

    // Breackfast3 は std::fmt::Display を実装しているので、"{}"が使える
    println!("朝食の種類:{}", b);
    println!("カロリー:{}", c);
```

## モジュール

person と animal のモジュールを作ってみた。

```rust
// person モジュール
mod person {
    pub struct Person {
        pub name: String, // pub を付けないと private です
        height: u32,
        weight: u32,
        weapon: String,
    }

    impl Person {
        // コンストラクタだが、 new に特に意味はない
        pub fn new(name: String, height: u32, weight: u32, weapon: String) -> Person {
            Person {
                name,
                height,
                weight,
                weapon,
            }
        }
        pub fn height(&self) -> u32 {
            self.height
        }
        pub fn weight(&self) -> u32 {
            self.weight
        }
    }

    // Person に Creature trait を実装する
    impl super::Creature for Person {
        fn walk(&self) {
            println!("人として歩きます")
        }
        fn weapon(&self) -> String {
            self.weapon.clone()
        }
    }
}

// animal モジュール
mod animal {
    pub struct Animal {
        pub name: String, // pub を付けないと private です
        height: u32,
        weight: u32,
        weapon: String,
    }

    // コンストラクタだが、 new に特に意味はない
    impl Animal {
        pub fn new(name: String, height: u32, weight: u32, weapon:String) -> Animal {
            Animal {
                name,
                height,
                weight,
                weapon,
            }
        }
        pub fn height(&self) -> u32 {
            self.height
        }
        pub fn weight(&self) -> u32 {
            self.weight
        }
    }

    // Animal に Creature trait を実装する(walk はデフォルトにして、weapon() だけ実装)
    impl super::Creature for Animal {
        fn weapon(&self) -> String {
            self.weapon.clone()
        }
    }

}

// Tに特に意味はない(Uでもいい。何らかの型という意味)。ここ(T:Crature)では、Creature trait を実装している型のこと
fn what_weapon_has<T:Creature>(t :T) -> String {
    t.weapon()
}
```

使ってみる。

```rust
    // モジュール person。new 関数を construct として使っている(が、別に new が特別なワードではない)
    let p = person::Person::new(String::from("Atsushi Kato"), 173, 65, String::from("fist"));
    println!("名前: {}", p.name); // name は pub なのでアクセス可能
    println!("身長: {}", p.height());
    println!("体重: {}", p.weight());

    // モジュール animal. new 関数を construct として使っている(が、別に new が特別なワードではない)
    let a = animal::Animal::new(String::from("Monkey"), 133, 45, String::from("fang"));
    println!("名前: {}", a.name); // name は pub なのでアクセス可能
    println!("身長: {}", a.height());
    println!("体重: {}", a.weight());

    // walk メソッドを使ってみる。これは trait(go の interface的なもの) Creature を実装している
    // trait では default を定義できるので、animal では実装していない
    p.walk();
    a.walk();

    // whtat_weapon_has は、Creature の trait を満たしていれば、使うことが出来る
    println!("{}",what_weapon_has(p));
    println!("{}",what_weapon_has(a));
```

## ベクタ

```rust
    // ベクタ
    let mut v: Vec<i32> = Vec::new();
    v.push(1);
    v.push(2);
    v[0] = 100;
    println!("{:#?}", v);

    let mut v = vec![0 ; 10]; // 0 ; 10 で 0 を 10こ作る
    v[0] = 101;
    v.push(1);
    v.push(2);
    println!("{:#?}", v);

    let r = v.get(100);
    let r = match r {
        Some(_) => n,
        None => { // 式も書けるよ
            println!("取得失敗");
            0
        },
    };
    println!("戻り:{}", r);
```

## ハッシュ

```rust
    // HashMap
    use std::collections::HashMap;

    let mut hash = HashMap::new();

    let n1 = 10;
    let n2 = 20;

    hash.insert("あいうえお".to_string(), n1);
    hash.insert("かきくけこ".to_string(), n2);

    println!("{:#?}", hash);
    println!("{:#?}", hash.get(&"あいうえお".to_string())); // get するときはキーの名前は参照で渡す必要がある

    // ハッシュの値を変更
    // あれば、値を変更する and_modify は無名関数(ココでは、|v| *v = 10000)が引数(複数行の時は、 {} を使う)を取る
    hash.entry("あいうえお".to_string()).and_modify(|v| *v=10000);
    // なければ、値を入れる or_insert はそのまま値を取れる
    hash.entry("ほげ".to_string()).or_insert(100);

    println!("{:#?}", hash);
```

## ファイル入出力

ファイルの読み書きをする場合は、

```rust
use std::fs::File;
```
して下さい。

下記、サンプル。

```rust
    let f = File::open("/etc/passwd");
    let _f = match f {
        Ok(file) => file,
        Err(error) => {
            panic!("ファイルopen時にエラー {:?}", error)
        }
    };

    // 面倒くさい場合
    let _f = File::open("/etc/passwd").unwrap();

    // 面倒くさいけど、メッセージくらいは出したい
    let _f = File::open("/etc/passwd").expect("ファイルオープンに失敗");

    let result = can_open_file("/etc/passwdz".to_string());
    match result {
        Ok(true)  => println!("ファイルOpen可能"),
        Ok(false) => println!("ファイル開けませんよ"), /* 意味ないのでは */
        Err(e)    => println!("ファイル開けません: {}", e)
    }

    let result = can_open_file_2("/etc/passwd".to_string());
    match result {
        Ok(true)  => println!("ファイルOpen可能"),
        Ok(false) => println!("ファイル開けませんよ"), /* 意味ないのでは */
        Err(e)    => println!("ファイル開けません: {}", e)
    }
}

fn can_open_file(path: String) -> Result<bool, io::Error> {
    let f = File::open(path);
    return match f {
        Ok(_file) => Ok(true),
        Err(e) => Err(e),
    };
}

fn can_open_file_2(path: String) -> Result<bool, io::Error> {
    let _f = File::open(path)?;
    Ok(true)
}
```

## 時間切れ

もう日が変わってしまうので、今日はここまで。
合宿以来全然触ってなかったので、すっかり忘れてしまっているのですが、面白みのある言語だと思うので、また機会をみてやってみようかと思います。

## 書いたコード

下記に上がっています。

https://github.com/ktat/rust_practice/blob/master/src/main.rs

## 謝罪

近年稀に見る、雑な記事で大変申し訳無く思っております...m(__)m