Rustの基本をPythonと比較しつつ学んでみる #1

普段Pythonなどをメインにお仕事をしていますが、Rustのごく基本的な文法や挙動などをPythonと比較しつつ学んでいってみます（前も入門書消化したタイミングで少し記事にしたりしていますが復習も兼ねて書いておきます）。

※Rust初心者なため誤解している点などがあるかもしれません。その辺はご容赦ください。

※長くなりそうなので記事を分割します。本記事は1記事目となります。

# 環境の準備

Rust自体のインストールやらrust-analyzer関係とかは以前記事にしていたのでそちらのリンクのみ貼っておきます。

https://qiita.com/simonritchie/items/dfe526be0410c7fb37cd

https://qiita.com/simonritchie/items/a8bdcbc65a0ae1861e6b

少しインストールされているバージョンが古くなっている気がするのでアップデートしておきます。

```
$ rustup update
```

バージョンを確認してみると以下のバージョンとなりました。

```
$ rustc --version
```

```
rustc 1.68.0 (2c8cc3432 2023-03-06)
```

また、記事執筆用に適当なプロジェクトをCargoで作成しておきます。

```
$ cargo new rust_basics
```

あとはVS Codeで対象のプロジェクトフォルダを追加して進めていきます。VS Code上でRun | Debugというメニューが出てくるのでそちらを使用して実行していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6f85100c-c21e-1222-a9dd-aed245c12392.png)

RustをVS Code上で触るのがかなり久々なので一応Cargoコマンドで作成された`main.rs`で実行してみてHello, world!が表示されることを確認しておきます。

```rust
fn main() {
    println!("Hello, world!");
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/32130c0e-ac0c-df2d-e01b-e2393026511c.png)

問題無さそうなのでこれで進めていきます。

# コンソール出力

Pythonの`print`関数に該当するものはRustでは`println!`となります。`!`はマクロ用の記述となり、`println!`関数というよりかは`println!`マクロという扱いになります。

```rust
fn main() {
    println!("Hello, world!");
}
```

```
Hello, world!
```

Pythonのように複数の引数を指定することはストレートにはできないようです（エラーとなります）。基本的に第二引数とかに関しては文字列のフォーマット処理用という感じのようです。

```rust
fn main() {
    println!("Hello", ", world!");
}
```

```
2 |     println!("Hello", ", world!");
  |              -------  ^^^^^^^^^^ argument never used
  |              |
  |              formatting specifier missing
```

Pythonの文字列のformatメソッドのように文字列中に変数などを入れたい場合には文字列中に`{}`の括弧を記述し、変数を第二引数以降に指定します。

```rust
fn main() {
    let cat_name: String = "タマ".to_string();
    let cat_age: i32 = 5;
    println!("猫の名前は{}です。歳は{}です。", cat_name, cat_age);
}
```

```
猫の名前はタマです。歳は5です。
```

Pythonのf-stringsのように変数を文字列内に挿入してコンソール出力で扱うような書き方もできます（v1.58.0以降でサポートされているようです）。

```rust
fn main() {
    let cat_name: String = "タマ".to_string();
    let cat_age: i32 = 5;
    println!("猫の名前は{cat_name}です。歳は{cat_age}です。");
}
```

```
猫の名前はタマです。歳は5です。
```

ただし`{}`の括弧内でPythonのようにメソッドを呼び出したりなどのプログラム制御は本記事執筆時点ではサポートしていない？ようです。例えば以下のように`{}`の括弧内でlenメソッドを呼びだしてみるとエラーになります。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    println!("{arr.len()}");
}
```

```
error: invalid format string: expected `'}'`, found `'.'`
 --> src\main.rs:3:19
  |
3 |     println!("{arr.len()}");
  |               -   ^ expected `}` in format string
  |               |
  |               because of this opening brace
  |
  = note: if you intended to print `{`, you can escape it using `{{`
```

このようなケースでは恐らく第二引数に値を指定するか一旦変数に別途設定する形が必要になるかと思われます。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    println!("{}", {arr.len()});
}
```

```
3
```

Pythonの文字列のformatメソッドのようにキーワード引数的な指定もできるようです。

```rust
fn main() {
    let cat_name: String = "タマ".to_string();
    let cat_age: i32 = 5;
    println!(
        "猫の名前は{cat_name}です。歳は{cat_age}です。",
        cat_name = cat_name,
        cat_age = cat_age,
    );
}
```

```
猫の名前はタマです。歳は5です。
```

余談ですが恐らく`println!`のlnは軽く調べた感じlineの意味かと思われます（新しい行に対して出力を行うという意味合い的に）。

# strとString

NumPyとかで固定長の文字列を扱うケースなどを除いて、Pythonだと文字列は基本的に可変長のstr型を扱う形となります。

一方でRustではstrは固定長の型となり、可変長の文字列は別途Stringという型があります。String型は文字列の長さの情報などを持ったポインタといった性質を持つため異なる文字列の長さを扱うことができます。

関数などで引数にstr型を指定すると、引数に指定される文字列の長さはケースバイケースで変動するため固定長のstr型では扱うことができずエラーになってしまいます。

```rust
fn print(input_str: str) -> () {
    println!("{input_str}");
}

fn main() {
    print("Hello, world!");
}
```

```
 --> src\main.rs:6:11
  |
6 |     print("Hello, world!");
  |           ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `Sized` is not implemented for `str`
  = note: all function arguments must have a statically known size
```

str型の代わりに可変長のString型を引数の型に指定しておくとコンパイルが通ります。ただし通常の`""`のクォーテーションで作成した文字列はstr型となるため`to_string`メソッドでString型に変換する必要があります。

```rust
fn print(input_str: String) -> () {
    println!("{input_str}");
}

fn main() {
    print("Hello, world!".to_string());
}
```

```
Hello, world!
```

もしくはstr型を使いつつも`&`の記号を引数部分に付与して借用とすることでもエラーを回避できます。値の変更などをしないケースであればこちらもシンプルかもしれません。

```rust
fn print(input_str: &str) -> () {
    println!("{input_str}");
}

fn main() {
    print("Hello, world!");
}
```

```
Hello, world!
```

# 整数と浮動小数点数間の計算

Pythonだとintやfloat同士を直接計算できます。例えば以下のような計算を行うことができます。

```py
value1: float = 10.5
value2: int = 20
value3: float = value1 + value2
print(value3)
```

```
30.5
```

一方でこういった計算はRustだとエラーで弾かれます。

```rust
fn main() {
    let value1: f64 = 10.5;
    let value2: i32 = 20;
    let value3: f64 = value1 + value2;
    println!("{value3}");
}
```

```
error[E0277]: cannot add `i32` to `f64`
 --> src\main.rs:4:30
  |
4 |     let value3: f64 = value1 + value2;
  |                              ^ no implementation for `f64 + i32`
  |
  = help: the trait `Add<i32>` is not implemented for `f64`
  = help: the following other types implement trait `Add<Rhs>`:     
            <&'a f64 as Add<f64>>
            <&f64 as Add<&f64>>
            <f64 as Add<&f64>>
            <f64 as Add>
```

こういった場合には型を揃える必要があるため、`20`といった整数値を`20.0`などに変更して浮動小数点数同士の計算となるように調整する必要があります。

```rust
fn main() {
    let value1: f64 = 10.5;
    let value2: f64 = 20.0;
    let value3: f64 = value1 + value2;
    println!("{value3}");
}
```

```
30.5
```

変数の型を途中で変えたくなった・・・といった場合には`as`を使ってキャストすることができます。

```rust
fn main() {
    let value1: f64 = 10.5;
    let value2: i32 = 20;
    let value3: f64 = value2 as f64;
    let value4: f64 = value1 + value3;
    println!("{value4}");
}
```

```
30.5
```

# 数値のbit数の差

Pythonだと（NumPyとかを使わなければ）整数などの数値の上限サイズをあまり意識しなくとも扱えます（必要に応じて大きなサイズが自動で割り振られて快適にプログラミングができる一方でメモリ的にはあまり効率的ではない面もあります）。

一方でRustでは数値のbit数を意識する必要が出てきます。8bitの整数であれば`i8`、32bitの整数であれば`i32`、64bitの浮動小数点数であれば`f64`、符号無しの8bitの整数であれは`u8`といった型を扱う必要があります。

変数で型の指定を省略した場合にはデフォルトでは整数であれば`i32`、浮動小数点数であれば`f64`の型が割り振られます。

bit数の範囲を超えた値を設定してしまうとエラーとなります。たとえば以下の例では`i8`を指定しつつ値に128を設定しているものの`i8`の型では-128～127までの範囲の値しか取れないのでエラーとなります。

```rust
fn main() {
    let value: i8 = 128;
    println!("{value}");
}
```

```
error: literal out of range for `i8`
 --> src\main.rs:2:21
  |
2 |     let value: i8 = 128;
  |                     ^^^
  |
  = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127`
```

# 配列とベクタ

Pythonではlistで動的に変更の効く配列のデータ、tupleで変更の効かない配列のデータを扱うことができます。

一方でRustだと`array`型の方は変更が効かない配列となりPythonのtupleに近い性質を持ちます。Rustの`Vec<T>`型の方が変更が効く配列となるためPythonのlistに近い挙動をします。

`array`型の方は`: [<値の型>; <値の件数>]`といった型の指定の仕方が必要になります。型と件数の間がコンマではなくセミコロンが必要なので注意してください。

たとえばi32の型の値を3件持つ配列の場合以下のような定義になります。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
}
```

上記のコードはPythonで言うとtupleを使う形で以下のようなコードに近い形になります。

```py
from typing import Tuple

arr: Tuple[int, int, int] = (10, 20, 30)
```

配列の長さはlenメソッドで参照することができます。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    println!("{}", arr.len());
}
```

```
3
```

また、iterメソッドでループを回すこともできます。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    for int_value in arr.iter() {
        println!("{int_value}");
    }
}
```

```
10
20
30
```

配列の値を直接println!マクロで表示することはできません。以下のようにエラーになります。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    println!(arr);
}
```

```
error: format argument must be a string literal
 --> src\main.rs:3:14
  |
3 |     println!(arr);
  |              ^^^
  |
help: you might be missing a string literal to format with
  |
3 |     println!("{}", arr);
  |              +++++

error: could not compile `rust_basics` due to previous error
```

f-stringsのような記述などでもエラーになります。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    println!("{arr}");
}
```

```
 --> src\main.rs:3:16
  |
3 |     println!("{arr}");
  |                ^^^ `[i32; 3]` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `[i32; 3]`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
```

こういった場合にはエラーメッセージにも書かれていますが`{:?}`という指定でフォーマットする必要があります。

```rust
fn main() {
    let arr: [i32; 3] = [10, 20, 30];
    println!("{:?}", arr);
}
```

```
[10, 20, 30]
```

Rustの配列は固定長でしたが、可変長のものを使いたい場合にはベクタ型の利用が必要になります。ベクタ型はPythonのlistに近い性質を持ちます。型の指定は`Vec<値の型>`といった記述が必要になります。

また、ベクタ型を生成するには`vec!`マクロなどを使います。他にも生成方法は色々ありますがこのマクロを使うことが多そうです。マクロの後には`[]`の括弧とともにコンマ区切りで初期値を指定します。

例えばi32の整数の値を格納するベクタ型で値が10, 20, 30の3つの値を持つベクタを作成するには以下のような記述になります。

```rust
fn main() {
    let int_vector: Vec<i32> = vec![10, 20, 30];
}
```

配列と同じようにベクタもiterメソッドでループを回したりlenメソッドでベクタの長さを取得したりなどができます。

```rust:ループを回す例
fn main() {
    let int_vector: Vec<i32> = vec![10, 20, 30];
    for int_value in int_vector.iter() {
        println!("{int_value}");
    }
}
```

```
10
20
30
```

```rust:lenメソッドで長さを取る例
fn main() {
    let int_vector: Vec<i32> = vec![10, 20, 30];
    println!("{}", int_vector.len());
}
```

```
3
```

Rustではデフォルトでは変数がイミュータブルで宣言されるのでベクタへ値を追加したい場合などには`mut`の指定を追加してミュータブルで変数を宣言する必要があります。追加に関してはpushなどのメソッドが用意されています（jsの配列などのようにベクタの末尾に追加されます）。

```rust
fn main() {
    let mut int_vector: Vec<i32> = vec![10, 20, 30];
    int_vector.push(40);
    for int_value in int_vector.iter() {
        println!("{int_value}");
    }
}
```

```
10
20
30
40
```

追加のメソッドとしてはextendやinsertなどの各種メソッドも他の言語と同じように用意されています。

# HashMap

Pythonの辞書に近い性質のものとしてHashMapがあります。キーと値のペアを持つデータとなります。

利用するには標準モジュールのものを読み込む必要があります。PythonだとimportでパッケージをインポートしますがRustでは`use`を使ってモジュールの読み込みを行います。

また、Pythonの辞書のような初期値の設定は行うことはストレートにはいかず生成後にinsertメソッドなどで値を追加する必要があるようです。

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

fn main() {
    let mut map = HashMap::new();
    map.insert("first_key", 10);
    map.insert("second_key", 20);
}
```

for文に渡すとキーと値それぞれが設定される形でループを回すことができます。Pythonだとitemsメソッドを使った時と同じような感じでしょうか。

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

fn main() {
    let mut map = HashMap::new();
    map.insert("first_key", 10);
    map.insert("second_key", 20);

    for (key, value) in map {
        println!("key: {key}, value: {value}");
    }
}
```

```
key: first_key, value: 10 
key: first_key, value: 10 
key: second_key, value: 20
key: second_key, value: 20
```

lenメソッドで配列やベクタと同様にデータの件数を取得することができます。

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

fn main() {
    let mut map = HashMap::new();
    map.insert("first_key", 10);
    map.insert("second_key", 20);

    println!("{}", map.len());
}
```

```
2
```

HashMapの内容をそのまま`println!`マクロで表示したい場合には配列やベクタと同様に`"{:?}"`というフォーマットの指定を使います。

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

fn main() {
    let mut map = HashMap::new();
    map.insert("first_key", 10);
    map.insert("second_key", 20);

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

```
{"first_key": 10, "second_key": 20}
```

`insert`メソッドを使わずにPythonのように初期化時に辞書の各値を設定したい場合には`[("キー1", "値1"), ("キー2", "値2"), ...]`という値を作成して、そちらを`.iter().cloned().collect()`のメソッドチェーンでHashMapに変換する・・・という形で近い形で書くこともできます。Rustビルトインだけだとこの辺は少し煩雑ではありますね（将来改善されるかもしれませんが・・・）。

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

fn main() {
    let map: HashMap<&str, i32> = [("first_key", 10), ("second_key", 20)].iter().cloned().collect();

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

```
{"first_key": 10, "second_key": 20}
```

`()`の括弧はPythonと同様にタプルを生成します。2件分の値を格納したタプルを指定することでキーと値として設定しています（タプルを格納した配列として扱っています）。

`iter`メソッドではその配列をイテレーターに変換しています。

`cloned`メソッドでは参照のコピーを作成するメソッドです。`clone`メソッドと似ていますが`clone`メソッドは値のコピーを作成するのに対して`cloned`メソッドは参照のコピーを作成します。どうやらイテレーターなどに対しては`clone`メソッドが使えないことがあるようなので`cloned`メソッドを使っています（`str`型の文字列などは`Clone`トレイトを持っておらず`clone`メソッドが利用できないので文字列の値を含むイテレーターなどでは`clone`メソッドが使えないようです。一方で`&str`型などは`Clone`トレイトを持っているため参照をコピーする形である`cloned`メソッドの方は利用できます）。

最後の`collect`メソッドはイテレーターからHashMapへと変換するメソッドです。

少し煩雑で考えることが多いので、現時点ではmaplitなどのサードのクレートとかを使わせていただくと快適なのかもしれません。

https://qiita.com/qryxip/items/7c16ab9ef3072c1d7199#maplit

HashMapの特定のキーの値にアクセスするにはgetメソッドを使います。Pythonの辞書に対するgetメソッドでキーが無い場合にNoneが返却される挙動と似たような感じでOption型で返却されるので扱うにはmatchステートメントで分岐させてNoneの場合とそれ以外の場合（Some）でそれぞれの分岐を記述する必要があります。

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

fn main() {
    let map: HashMap<&str, i32> = [("first_key", 10), ("second_key", 20)].iter().cloned().collect();
    let value: Option<&i32> = map.get("first_key");
    match value {
        Some(int_val) => println!("{int_val}"),
        None => println!("キーが見つかりませんでした。"),
    }
}
```

```
10
```

# 参考文献・サイトなど

https://www.amazon.com/Speed-Your-Python-Rust-performance/dp/180181144X/

https://qiita.com/DanYuya/items/1b3a9fbd9cef7047b3ca

https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html

https://qiita.com/qryxip/items/7c16ab9ef3072c1d7199