PythonからRustを呼んで高速化！ PyO3 チュートリアル：クラスをラップする その➁

## 概要

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

これは、[PythonからRustを呼んで高速化! PyO3 チュートリアル：クラスをラップする その➀](https://qiita.com/kenmaro/items/8ff37204feb37a4edbae)

の続きになります。





## 目標
目標は、
`Rustのクラス（struct + method）を定義し、それをPythonから呼ぶ`
でしたが、
前回でRustで書かれたクラスをPythonから呼ぶ方法を解説しました。

`クラスのコンストラクタ`および、`プロパティのgetter,setter`もPythonからPyO3経由で呼ぶことができました。


今回は、クラスメソッドをいくつか追加して、`RustからPythonへの型にPyO3経由で変換する`ところを解説していきます。

**PyO3のgitレポジトリ**([ここ](https://github.com/PyO3/pyo3))を参照し、

`RustのオブジェクトをPython側にどうPyO3で引き渡すか`を解説します。

- `Vec` -> `List`
- `Hashmap` -> `Dict`
- `Vec` -> `Tuple`

などです。




## 手順
前回までに`cargo new --lib example`で作ったプロジェクトを使用します。
新しく作ってももちろん問題ありません。

おさらいとして、前回はクラスのコンストラクタ、プロパティの`num`の`getter`, `setter` をPyO3経由でPythonから呼べるようにしました。
以下が、前回完成させたコードです。

```rust

//lib.rs
use pyo3::prelude::*;
use pyo3::{wrap_pyfunction};


// ======================RUST CLASS TO PYTHON ======================================
/// Class for demonstration
// this class, MyClass can be called from python
#[pyclass(module = "my_class")]
struct MyClass {
   num: i32,
   debug: bool,
}

#[pymethods]
impl MyClass{
    #[new]
    fn new(num:i32, debug:bool) -> Self{
        MyClass{
            num: num,
            debug: debug
        }
    }

    #[getter]
    fn get_num(&self) -> PyResult<i32>{
        Ok(self.num)
    }

    #[setter]
    fn set_num(&mut self, num: i32) -> PyResult<()>{
        self.num = num;
        Ok(())
    }
}


// =================CREATING MODULE FOR PYTHON=========================
/// This module is a python module implemented in Rust.
#[pymodule]
fn test_library(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_wrapped(wrap_pyfunction!(get_prime_numbers))?;
    m.add_class::<MyClass>()?;

    Ok(())
}

```

この`MyClass`に、今回は関数を６つ追加します。
早速ですが、以下がコードです。

```rust

//lib.rs

use pyo3::types::PyType;
use pyo3::types::PyInt;
use pyo3::types::PyList;
use pyo3::types::PyTuple;
use pyo3::types::PyDateTime;
use std::collections::HashMap;

#[pymethods]
impl MyClass{

    fn test1(&self) -> PyResult<bool>{
        if self.num > 3{
            Ok(true)
        }else{
            Ok(false)
        }
    }

    fn test2(&self) -> PyResult<String>{
        if self.debug == true{
            let result: &str = "your debug is True";
            Ok(result.to_string())
        }else{
            let result: &str = "your debug is False";
            Ok(result.to_string())
        }
    }

    fn test3<'py>(&self, py: Python<'py>) -> PyResult<&'py PyList>{
        let mut vec = vec![1,2,3];
        let result = PyList::new(py, &vec);
        Ok(result)
    }

    fn test4(&self, py: Python) -> PyResult<PyObject>{
        let mut map = HashMap::new();
        map.insert("key1", 1);
        map.insert("key2", 2);
        map.insert("key3", 3);
        assert_eq!(map["key1"], 1);
        assert_eq!(map["key2"], 2);
        assert_eq!(map["key3"], 3);
        Ok(map.to_object(py))

    }

    fn test5(&self) -> PyResult<f64>{
        let result:f64 = 1.23;
        Ok(result)
    }

    fn test6<'py>(&self, py: Python<'py>, dt: &PyDateTime) -> PyResult<&'py PyTuple>{
        let mut vec = vec![3,4,5];
        let result = PyTuple::new(py, &vec);
        Ok(result)

    }
}
```

### fn test1
`Rustのbool`を、`Pythonのbool`へとPyO3経由で受け渡ししています。


### fn test2
`RustのString`を、`Pythonのstr`へとPyO3経由で受け渡ししています。

### fn test3
`RustのVec`を、`PythonのList`へとPyO3経由で受け渡ししています。
この書き方がきちんと理解できてません。。

### fn test4
`RustのHashmap`を、`PythonのDict`へとPyO3経由で受け渡ししています。

### fn test5
`Rustのf64`を、`Pythonのfloat`へとPyO3経由で受け渡ししています。

### fn test6
`RustのVec`を、`Pythonのtuple`へとPyO3経由で受け渡ししています。この際、関数の引数として、`PythonのDatetime`を受け取っています。
この書き方がきちんと理解できてません。。


## testを実行する
test3, test6に関しては、正直書き方がうまく理解できていませんが、テストを実行します。
前回と同じように`Cargo.toml`、`setup.py`を用意することで、

```bash
python setup.py install
```

でビルドすることができます。

その後、`test.py`を以下のように準備し、テストを実行します。

```test.py

import test_library 


if __name__  == "__main__":

    # Testing class
    print("\ntest for class")
    num = 2
    debug = True
    test = test_library.MyClass(num=num, debug=debug)

    print(test.num) # getter test
    test.num = 4    # setter test
    print(test.num)
    result = test.test1()
    print(result)
    print(type(result))

    result = test.test2()
    print(result)
    print(type(result))

    result = test.test3()
    print(result)
    print(type(result))

    result = test.test4()
    print(result)
    print(type(result))

    result = test.test5()
    print(result)
    print(type(result))

    import datetime 
    now = datetime.datetime.now()
    result = test.test6(now)
    print(result)
    print(type(result))
```



## まとめ

今回は、クラスメソッドをいくつか追加して、RustからPythonへの型にPyO3経由で変換するところを解説しました。
`Cython`よりは比較的わかりやすい感覚があるものの、`PyO3`自体バージョンがどんどん更新されているため、一番いいのはバージョンをきちんと意識して（Fixして）開発するか、
もしくはGitをきちんと追い、APIの呼び方の変更にきちんと気を配るべきでしょう。

ただ、Rust面白いので、これからも勉強していこうと思います。


今回はこの辺で。

おわり。