numクレートを使って数値計算を複数の型に対して記述する

この記事は[「OPTIMIND x Acompany Advent Calendar 2021」](https://qiita.com/advent-calendar/2021/optimind_acompany) 6日目の記事です。

こんにちは。オプティマインドの菊池です。
Rustに関する小ネタとして、num クレートについて書きたいと思います。

### ジェネリクスを用いたadd関数

皆様は、業務中や趣味のプログラミング、あるいはトイレ、夢の中などで、関数を書くことがしばしばあると思います。

そうした関数漬けの日々を送るあなたは、あるRustによる開発プロジェクトの中で以下のような革命的な関数を世に産み落としました。

```rust
fn add(a: u128, b: u128) -> u128 {
    a + b
}
```

この関数はあまりにも革命的なので、プロジェクトコードのあらゆる箇所で使われるようになったのですが、ある日、ちょっと困ったことが起きました。このadd関数を、`usize`型の整数に対しても使いたいという要望が出てきたのです。

最も簡単な解決策は、下記のようにusize用の別の関数をもう一つ作ってしまうことです。

```rust
fn add_usize(a: usize, b: usize) -> usize {
    a + b
}
```

これで足元の課題は解決しましたが、今後`u64`でも使いたい、`i128`でも使いたいなどといった要望が出てくるたびに関数を作っていたのではとても面倒です。できれば、何度も実質的に同じ振る舞いを記述せずに済ませたいところです。

幸い、Rustはジェネリクスの機能を持っているため、`std::ops::Add`トレイトを実装した型というジェネリック境界により、以下のように汎用的に記述することができます。

```rust
fn add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T {
    a + b
}
```

これで、`std::ops::Add`を実装している型であれば、どんな型でもadd関数を使うことができます。

### ceil関数の場合は?

同じことを、以下のceil関数でも試してみたいと思います。(これはa/bの整数切り上げを計算する関数です。[こちら](https://qiita.com/T_Tag/items/6f980fcec4ffafb8588d)などを参照。)

```rust
fn ceil(a: u128, b: u128) -> u128 {
    (a + b - 1) / b
}
```

この関数の中では、加算、減算、除算が使われているので、各演算に対応したトレイト`std::ops::Add`、`std::ops::Sub`、`std::ops::Div`と、2度使っている`b`のために`Copy`トレイトを実装してやればよさそうです。

それを実装したのが以下です。

```rust
fn ceil<
    T: Copy + std::ops::Add<Output = T> + std::ops::Sub<Output = T> + std::ops::Div<Output = T>,
>(
    a: T,
    b: T,
) -> T {
    (a + b - 1) / b
}
```

しかし、このコードをコンパイルしてみると、以下のようなエラーが起こってしまいました。


>  |  (a + b - 1) / b
>  | _______^ expected type　parameter `T`, found integer
>  |
>    = note: expected type parameter `T`
>                        found type `{integer}`
 
コード中に登場する整数`1`について、コード中に具体的な型を特定する手段がなく何らかの整数型としか類推できないため、コンパイルエラーとなってしまいます。このように、処理の中に実際の数値が登場する場合は、addと同じ方法では記述できません。これは困りました。

### numクレートでceil関数を実装

このようなケースで役に立つのが、[numクレート](https://docs.rs/num/latest/num/)です。numクレートはcargo.tomlに以下のように記述することで利用することができます。

```
[dependencies]
num="0.4.0"
```

このクレートを使うと、先ほどの`1`の部分は`T::one()`と書くことができ、ceil関数は以下のようになります。

```rust
fn ceil<T: Copy + num::Integer>(a: T, b: T) -> T {
    (a + b - T::one()) / b
}
```

`1`の部分だけでなく、トレイト境界の記述も非常にスッキリしています。numクレートはこのように、数値の計算に関して直感的に利用しやすいトレイトを提供しています。

### numクレートに実装されている値

numクレートには数値として、`Zero`トレイトと、`One`トレイトが用意されています。それぞれ`::zero()``::one()`で、各型の0、1の値を呼び出すことができ、これらは`num::Num`や`num::Integer`などに実装されています。

```rust
fn do_something<T: num::Num>() {
    let zero = T::zero();
    let one = T::one();
}
```

2、3などの数字を扱いたい場合も、以下のようにして実現することができます。

```rust
fn do_something<T: Copy + num::Num>() {
    let one: T = T::one();
    let two: T = one + one;
    let three: T = one + two;
}
```

### numクレートに含まれている便利なトレイト・構造体

最後に、numクレートに含まれているトレイト・構造体で、便利なものをまとめておきます。

#### num::Num
整数型、浮動小数点数型を含む数値全般を扱うトレイト。加減乗除と剰余の演算、PartialEq、Zero、Oneが実装されている。

#### num:Integer
整数型を扱うトレイト。num:Numの実装されているトレイトに加え、Eq、Ordなどが実装される。

`is_even`、`is_odd`、`gcd`、`lcm`、`extended_gcd`などのちょっとしたメソッドも実装してくれる。

#### num::Float
浮動小数点数型を扱うトレイト。num:Numの実装されているトレイトに加え、Negなどが実装される。

#### num::BigInt

巨大な整数型を扱うことができる型。

```rust
let mut x: BigInt = 1.into();
for i in 2..50 {
    x *= i;
}
println!("{:?}", x); // 608281864034267560872252163321295376887552831379210240000000000
```

#### num::Complex

複素数を扱うことができる型。

```rust
let mut x: Complex<i128> = Complex::new(10, 10);
let mut y: Complex<i128> = Complex::new(2, 5);
println!("{}", (x * y));        // -30+70i
println!("{}", (x * y).conj()); // -30-70i
```

### 最後に宣伝

株式会社オプティマインドでは、一緒に働く仲間を大募集中です。
カジュアル面談も大歓迎ですので、気軽にお声がけください。

【エンジニア領域の募集職種】
●[ソフトウェアエンジニア](https://hrmos.co/pages/optimind/jobs/0002012)
●[QAエンジニア](https://hrmos.co/pages/optimind/jobs/0002011)
●[Androidアプリエンジニア](https://hrmos.co/pages/optimind/jobs/0002008)
●[組合せ最適化アルゴリズムエンジニア](https://hrmos.co/pages/optimind/jobs/0002013)
●[経路探索アルゴリズムエンジニア](https://hrmos.co/pages/optimind/jobs/0002014)
●[バックエンドエンジニア](https://hrmos.co/pages/optimind/jobs/0002015)
●[インフラエンジニア](https://hrmos.co/pages/optimind/jobs/0002016)
●[UXUIデザイナー](https://hrmos.co/pages/optimind/jobs/0002010)

【ビジネス領域の募集職種】
●[セールスコンサルタント](https://hrmos.co/pages/optimind/jobs/0002001)
●[採用・人事](https://hrmos.co/pages/optimind/jobs/0002002)

『オプティマインドってどんな会社？』については、[こちら](https://www.optimind.tech/recruit/)から
Wantedlyでも[こちらで募集中](https://www.wantedly.com/companies/optimind/projects)