PyO3でPythonからRustで作ったライブラリを呼ぶ

Feb. 16, 2019, 8:08 a.m. edited Feb. 23, 2019, 12:32 a.m.

#PyO3  #Rust  #シミュレーション  #Python 

Python,手軽で便利ライブラリたくさんあって良いですね.
Rust,厳格で素早くて良いですね.

一緒に使えれば素晴らしいですね1

ということで,これを実現するライブラリであるPyO3を使ってみました2.他にもrust-cpythonも有名ですが,更新頻度やスター数からPyO3に勢いが出ているので,今回はPyO3を選びました.

Rustのバージョンはrustc 1.34.0-nightlyで試しました.nightly3じゃないとPyo3は動かないようです.なぜならspecializationを使ってるから.(この点rust-cpythonはstableで動くんですけどねー.それでもPyO3の方がスターが多くなるのはこの界隈が良い意味で発展中ということなのかな)

まずは
cargo new python_rust_example --lib
でライブラリ用のプロジェクトを作ります.

それから,Cargo.toml

[package]
name = "python_rust_example"
version = "0.1.0"

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

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

と記述します.ここで,バージョン0.5.2を指定したのは次の記事でNumPyと一緒に使うためのバージョン合わせです(あと,最新バージョンのReadme.mdのサンプルが動かなかったのも理由としてある)

そして,試しに値を2つ渡して和を返す関数として,

#![feature(proc_macro)]

extern crate pyo3;
use pyo3::prelude::*;

#[pymodinit]
fn python_rust_example(_py: Python, m: &PyModule) -> PyResult<()> {
    #[pyfn(m, "add_double")]
    fn add_double_py(_py: Python, a: f64, b: f64) -> PyResult<f64> {
        Ok(a + b)
    }

    Ok(())
}

のようにadd_double()を作ります.pyfn()に渡す文字列引数がpythonから呼ぶときの関数名となります.

次にビルドします.

ここで,Macユーザーは.cargo/config.cargoがなければmkdirする)に

[target.x86_64-apple-darwin]
rustflags = [
    "-C", "link-arg=-undefined",
    "-C", "link-arg=dynamic_lookup",
]

としてコンパイル時のパラメータを渡してやる必要がある模様.

そして,cargo build --releaseでビルド!

ビルド完了したら,Macユーザーは

cp targets/release/libpython_rust_example.dylib python_rust_example.so

Linuxユーザーはそのまま

cp targets/release/libpython_rust_example.so libpython_rust_example.so

Windowsユーザーはtargets/release/libpython_rust_example.dllstring_sum.pydへコピー.

あとは最後にPythonシェルで

>>> from python_rust_example import add_double
>>> add_double(3, 5)
8.0
>>> add_double('hoge', 2)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    add_double('hoge', 2)
TypeError: must be real number, not str

と確認して完了.ちゃんと他の型を渡すとエラーを出してくれますね.

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


  1. Rustで素早くシミュレーションして結果をPythonに返してパラメータ探索,とかとりあえず思いついた 

  2. 実はPyO3のReadmeドキュメントと数時間格闘して全くビルドできず,なぜだろうと思ったらみんなalpha版で,https://crates.io/crates/pyo3を見てやっと動くものを得た,という経緯.もっともrust-numpyと合わせたかったので,結局バージョン0.5.2のガイドを見つけて参考にして書いた 

  3. Rustのnightlyとstableを手軽に切り替えるのにrustupがとても便利