PythonからRustを呼んで高速化！ PyO3 チュートリアル：簡単な関数をラップする　その➀

## 概要

1.　Pyhtonでアルゴリズムまで書いてあるのは速度面では好ましくないな〜
2.　よし、C, C++あたりで書いてあるものを探して、それをPythonから呼んで高速化しよう。
3.　なかなかいいライブラリ見つからんな、
4.　おっ、Rustていう言語で書かれてるのならあったぞ
5.　RustてPythonから呼べんのか？？

## 最終的な目標

というわけで、今までC++のライブラリをPythonから呼んで高速化を図るための「Cython」チュートリアルを書いてきましたが、

今回からはRustをPythonから呼んで高速化するための「PyO3」というライブラリのチュートリアルを書いていきます。

最終的な目標は、
```Rustで書いた関数やクラス（的なもの？）をPythonから気軽に呼べるようになること```
です。



このPyO3([gitはここ](https://github.com/PyO3/pyo3))は、開発の真っ只中であり、バージョン更新が（おそらく）凄まじい速度で行われております。




## 今回の目標
そもそもRust初見の状態からスタートして、
PythonからRustで書いた関数を呼べるようになる、という目標のもとPyO3を触ってみました。

キータにも、他ブログでもいくつかの解説記事が出ております。
- [[Rust] PyO3 で Python パッケージを作成](https://qiita.com/osanshouo/items/671888bdd6afeec1e939)
- [Pyo3でPythonとRustを連携させる](https://qiita.com/ta_to_co/items/d819ceb3d409fd9bd732)
- [RustモジュールをPythonから実行する (PyO3)](https://ohke.hateblo.jp/entry/2020/02/15/230000)
- [PythonとRustを使ってPythonの拡張モジュールを書く](https://logmi.jp/tech/articles/313765)


今回は、
[RustモジュールをPythonから実行する (PyO3)](https://ohke.hateblo.jp/entry/2020/02/15/230000)
さんのコードをお借りして、それにsetup.pyを付け加えて、Rustで書かれた関数をPythonから呼んでみたいと思います。


## 手順

>rustのインストール


１行目を実行するとインストールについて３択で聞かれますがdefaultインストールの１を選んで問題ないかと思います。

```bash
curl https://sh.rustup.rs -sSf | sh
source $HOME/.cargo/env
```


>rust nightlyのインストール

PyO3を使用するために必要になります。
開発中のクレートをたくさん含んだβバージョンと言うようなものでしょうか。

>バージョンを確認してみる
このバージョンを今回は使います。

```bash
$rustc --version
rustc 1.44.0-nightly (f509b26a7 2020-03-18)
$ rustup --version
rustup 1.21.1 (7832b2ebe 2019-12-20)
```



```bash
rustup install nightly
rustup default nightly
```


>rust のプロジェクトを作る

`--lib` をつけることで、ライブラリ用のプロジェクトを作ります。
今回は、プロジェクト名を`example`としました。

```bash
cargo new --lib example

$ tree example/
├── Cargo.toml
├── setup.py
├── src
│   └── lib.rs
```

フォルダ構造はこのようになっています。

## Cargo.tomlを設定する
RustがC++などより優れていると言われていることのひとつ、ライブラリの管理の手軽さがここにあります。
`Cargo.toml`に必要なライブラリを書くのですが、`CMakeLists.txt`とかよりはるかに簡単で見やすいです。

```Cargo.toml
[package]
name = "test"
version = "0.1.0"
edition = "2018"

[lib]
name = "test_library"
crate-type = ["cdylib"]

[dependencies.pyo3]
version = "0.9.1"
features = ["extension-module"]
```

今回はpyo3をライブラリとして使用し、
作る目的のライブラリは、`test_library`です。

すなわち、Pythonで

```test.py
import test_library
```

みたいに書きたいわけです。


## Rustで関数を作る

>とりあえず写経してみる

コードは[RustモジュールをPythonから実行する (PyO3)](https://ohke.hateblo.jp/entry/2020/02/15/230000)
さんのものを丸々持ってきました。
エラトステネスのふるいの実装だそうです。

```rust

//lib.rs

use pyo3::prelude::*;


// This is the test function in Rust, getting prime number, which is called by python as get_prime_numbers
#[pyfunction]
fn get_prime_numbers(n: i32) -> PyResult<Vec<i32>> {
    let mut flags = Vec::new();
    for _ in 0..n+1 {
        flags.push(true);
    }

    let upper = (n as f32).sqrt().floor() as i32;
    for i in 2..upper+1 {
        if !flags[i as usize] {
            continue;
        }

        let prime = i;

        let mut j = prime * 2;
        while j <= n {
            flags[j as usize] = false;
            j += prime;
        }
    }

    let mut primes = Vec::new();
    for i in 2..n+1 {
        if flags[i as usize] {
            primes.push(i);
        }
    }

    Ok(primes)
}



```

Rustの構文を細かく解説できるだけの能力がありませんので、
この関数はあっているものとし、どうこれがPythonから呼べる形にラップされているかというところを解説します。

```rust
#[pyfunction]
fn get_prime_numbers(n: i32) -> PyResult<Vec<i32>>
```
をみてみると、
まず```#[pyfunction]```のデコレータ的なものがこの関数の引数もしくは返り値にPyO3によりパースされたPythonオブジェクトが入ることを宣言しています。

この```get_prime_numbers```関数はint32を引数に取り、それ以下の素数をリストとして返す関数です。
このとき、返り値のところが、```PyResult<Vec<i32>>```となっていますが、
この```PyResult```は```use pyo3::prelude::*;```から来ています。

また、```Vec<i32>```は、下の型の対応表のようになっており、PyResut によってPythonのList型に変換されます。


| Rust            | Python |
|:---------------:|:------:|
|`i32`, `usize` 等|`int`   |
|`f32`, `f64`     |`float` |
|`bool`           |`bool`  |
|`Vec<T>`         |`list`  |
|`String`         |`str`   |
|`HashMap`        |`dict`  |

> [[Rust] PyO3 で Python パッケージを作成]からの引用　(https://qiita.com/osanshouo/items/671888bdd6afeec1e939)

また、最後の

```rust
Ok(primes)
```

が```Vec```を```list```へと変換しています。


## まとめ
ここで終わると参考文献をほとんどコピペしたものになって申し訳ないのですが、
一度ここで区切ります。

まとめとして、

- Rustのインストールを行った。
- Rustのライブラリ用のプロジェクトを作成した。
- Cargo.tomlを書いた。
- 関数を写経し、Rust->Pythonの型変換を確認した。

以上になります。

次は`lib.rs`を追記し、`setup.py`も加え実際にライブラリのオブジェクトファイルとしてコンパイルしていきます。

今回はこの辺で。


おわり。