普段はPython使いなのですが、可視化部分等はPythonでそれ以外はRustで高速できないかと考え調べてみました.

今回はPyo3を使ってRustとPythonをつないでみました.

Pythonのモジュールとして使われるように,Rustのコードを書いてます

ドキュメントはあるものの、情報量もまだまだ足りないので,わからない所含めまとていきます.

ちなみにRustとPyo3の記事だと

    • [Rust] PyO3 で Python パッケージを作成

 

    PyO3とrust-numpyを使ってPythonからNumPyをRustに渡して操作する

を参考にしました.

注意 rustのバージョン
現在,pyo3を使うにはnighty 版が必須です.
これはissueにも記載がありますが,いくつか必要な機能がstableでは提供されていないためです.

Cargo.toml

公式のサンプルを例にPyo3を使うのに必要な設定について説明します.

[package]
name = "string-sum"
version = "0.1.0"
edition = "2018"

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

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

使うセクションは

    • [package]

 

    • [lib]

 

    • [dependencices]

 

    の三種類です.

[package]はcrate用のセクションで,cargoを使ったことがあれば実際にわかると思います.
変数は以下になります.

    • name: rustで参照するpackage名

 

    version: アプリケーションのバージョン

[lib]はライブラリの出力のセクションです.

    • name:build時に作れるファイルの名前です.

 

    crate-type: 出力のタイプを指定するものです.

Pythonで使うなら、基本Python用の動的ライブラリになるので、[“cdylib”]を指定すればよいです.
詳細はここを御覧ください.

[dependencies] は依存ライブラリのついて記載する所です.

    • dependencies.pyo3でpyo3に関する条件を記載する箇所になります.

 

    • versionは指定するバージョン

 

    featuresは条件つきコンパイルを指定する所で今回は外部モジュールとしてコンパイルすることを指定しています.

なので、実際にpyo3を使って開発したい場合はCargo.tomlからnameだけ変更すればよいのかと思います.

実際のソース

実際にPythonから呼び出す方法を公式のサンプルから説明します.

まずPythonのモジュールとしてみせたいものを
#[pymodule] をつけた関数として定義します.
引数の型は{ythonのプロセスを表すPython型と, Pythonのモジュールを表すPyModule型になります.

Rustのattributeは基本決まっていますが,
proc_macro_attributeを使って新たに作ることができます.(nighty版だけの機能のようですが)

attributeはPythonでいうデコレータになります.おかげで特に何も考えずに使えます.

実際にpythonから呼び出したいオブジェクトを定義してきます.

関数を呼び出す場合

Pythonから呼び出す関数の場合は #[pyfunction] を設定すればよいです.

公式そのままですが、以下のようにすれば動きます.

use pyo3::prelude::*;
use pyo3::wrap_pyfunction;

#[pyfunction]
/// Formats the sum of two numbers as string
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
    Ok((a + b).to_string())
}

/// This module is a python module implemented in Rust.
#[pymodule]
fn string_sum(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_wrapped(wrap_pyfunction!(sum_as_string))?;

    Ok(())
}

注意点ですが, 返り値はPyResultにする必要があります.
また,OK()は返り値をPyResultにしてくれます.
返り値がない場合は,Ok(())とし、型としてはPyResult<()>とすればよいです.

配列の返却方法

さっきの例だとPython側ではStringが返ります.
今度はPython側でlistを返してみます.

#[pyfunction]
fn make_list<'p>(py: Python<'p>) -> PyResult<&'p PyList> {
    let v1 = vec![vec![vec![99; 3]; 2]; 10];
    let list = PyList::new(py, v1);
    Ok(list)
}

でできます.

注意

    • listの場合はPyList型にする必要があります.

 

    • #[pyfunction] が着いている場合,引数:py は普通は指定する必要がありません.

 

    • ただlistの場合はnewで引数として渡す必要があるため,指定しています.

 

    • ライフタイム’pはpyfunctionとして使用する限りは不要のようです.

 

    • ただ,#[pymethods]で実行する場合、ライフタイムを指定しないとエラーになります.

 

    ソースをおえていないので実態はわからないのですが,pyfunctionはpymoduleにライフタイムが紐づく?ので,ライフタイムを指定せずともエラーにならないのかなと思いました.

クラスの場合

Rust側ではclassを返却するには二つの処理が必要です.

#[pyclass]をつけた構造体の定義

m.add_class::<クラス名> をpymodule内に定義する.

これができれば,動きます.

    newの設定方法

#[new]をつけて以下のようにnewメソッドで実行すると,new相当になります.
実際に例を書くと以下みたいな形になります.

#[pyclass]
pub struct Hoge {
    pub x: usize,
}

#[pymethods]
impl Hoge {
    #[new]
    fn new(obj: &PyRawObject, x: usize){
            obj.init({
            Hoge {
                x: x,
            }
        });
    }

他にもgetter,setterが定義できたりします.

Numpy

ndarrayとやり取りできるはずですが、少し頑張ってもエラーになったので、今回はスキップします.
githubはあるものの、そこのexampleも
#![deny(rust_2018_idioms)]になっていて,これを外すと動きませんでした.

テスト

どうやらPyo3がある状況だとMacでCargo testをするとエラーになるようです.
以下のみの実行がエラーになります.(他のPyo3が使わないプロジェクトでエラーにならないことは確認しました)

#[cfg(test)]
    #[test]
    fn it_works() {
        assert!(false);
    }
    Finished dev [unoptimized + debuginfo] target(s) in 3.19s
     Running target/debug/deps/python_rust_example-3e2a90b01ebe111d
dyld: Symbol not found: _PyExc_OverflowError
  Referenced from: python_rust_example/target/debug/deps/python_rust_example-3e2a90b01ebe111d
  Expected in: flat namespace
 in python_rust_example/target/debug/deps/python_rust_example-3e2a90b01ebe111d
error: process didn't exit successfully: `python_rust_example/target/debug/deps/python_rust_example-3e2a90b01ebe111d` (signal: 6, SIGABRT: process abort signal)

この辺は解決するべきですが,その前に夏休みが終わってしまったので,今回はこの辺でお別れです(涙)

まとめ

今回は,最低限Pyo3の使い方を解説しました.
Pyo3はまだまだ枯れきっていないのか,調べてみるとかなりトラブルが発生しました.
そうしたトラブル自体が勉強になることもあるので、自分でも使い込みつつどこかでコミットできたらなと思います.

广告
将在 10 秒后关闭
bannerAds