PyO3を使ってPythonからRustの関数を呼び出してみる

今回は、表題の通り、PyO3を使ってPythonからRustの関数を呼び出してみたのでその備忘録です。

初めは難しいのかなぁと思いましたが、やってみると結構簡単に出来ました(まだ簡単なところしかやってないからかもしれませんが、笑)

まずはコマンドプロンプト等からcargo new rs2py --libと打ち、今回のを試してみる用のライブラリクレート「rs2py」を作ります。その後、作成された「rs2py」内のtomlファイルを以下のように記述します。

[package]
...(色々書かれている)

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

[dependencies]
 pyo3 = { version = "0.15.1", features = [ "extension-module", ] }

tomlファイルでは、crate-type = ["cdylib"]とし、で共有ライブラリを作るようにします。 後はpyo3の依存を追加します。 ちなみに、以前はpyo3はnightly-rustでしか使用できなかったみたいですが、今は(pyo3のバージョンが0.11から)stableなRustで扱えるようになったらしいです。

さて、これでtomlファイルの準備は完了しましたので、次はlib.rsを書いていきます。 今回は初めての挑戦ということで、呼ばれたら「Hello, world!」と出力されるような簡単な関数を記述していきます。

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

#[pymodule]
fn rs2py(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_wrapped(wrap_pyfunction!(hello))?;

    Ok(())
}
#[pyfunction]
fn hello() -> PyResult<()> {
    println!("Hello, world!");
    Ok(())
}

このコードの中で大切な点としては

  • Python から呼び出したい関数には #[pyfunction]を書く
  • Pythonのモジュールとしてみせたいものには#[pymodule] をつけた関数として定義する
  • 戻り値は PyResult にする必要がある(戻り値のない場合は PyResult<()>)

みたいです。この辺りは私もまだ知識不足で正しく理解できていませんが。。。

さて、lib.rsファイルも書けたらcargo build --releaseでビルドをしましょう。 ビルドをすると、target\releaseフォルダに色々なファイルが出来ますが、その中の「rs2py.dll」が、私達が欲しいものです。これを「rs2.py.pyd」と拡張子を変更してください。

これでライブラリの準備ができましたので、pythonで呼び出せることを確認しましょう。同じフォルダに適当な名前でPythonファイルを作成し、中に下記のようなコードを書きましょう。

import rs2py

rs2py.hello()

書けたらそのPythonファイルを実行してみてください。「Hello, world!」と表示されたら成功です。

今回はここまで、これをきっかけにして今後RustとPythonで色々連携できるようにしたいと思います。