Python から Rust の関数を呼び出すための手順メモです。
このように別言語のインタフェースを呼び出すことを FFI (Foreign Function Interface) といいます。
Rust の関数を Python から呼び出す手順
Rust コードから動的リンクライブラリを生成する
2つの整数の和を計算するような、簡単な Rust 関数を例にします。
以下のコードを lib.rs
として保存します。
#[no_mangle]
pub extern "C" fn add_int(a: i32, b: i32) -> i32 {
a + b
}
次のコマンドで、C のインタフェースに対応した動的リンクライブラリを生成することができます。
$ rustc --crate-type=cdylib -o libadd.so lib.rs
$ ls libadd.so
libadd.so
#[no_mangle]
今回定義したメソッドには no_mangle
のアトリビュートを定義しています。
これを定義しないと、関数名 add_int
がコンパイラによって都合の良いように改変されてしまいます。
これをマングルといいます。
今回は Python から add_int という名前でメソッドにアクセスしたいので、この定義によってマングルされるのを防ぎます。
extern “C”
もうひとつ、extern "C"
という宣言もあります。
これは ABI を C 方式にしろ、という指示になります。
ABI とは Application Binary Interface の略です。
例えば、引数や戻り値の渡し方のルールなどをコントロールします。
今回の例では extern “C” がなくても実害はなさそうですが、C言語の FFI を利用する場合は明示的に宣言するのが適切です。(今回は Python の C言語 FFI ライブラリを利用しようとしています)
Python から動的リンクライブラリを呼び出す
生成した libadd.so
ファイルを、Python コードの ctypes ライブラリで読み込みます。
次のように、2つの整数の和が計算されます。
from ctypes import cdll, c_int
lib = cdll.LoadLibrary("./libadd.so")
lib.add_int.argtypes = [c_int, c_int]
lib.add_int.restype = c_int
print(lib.add_int(1, 2))
$ python3 main.py
3
ここでは、Rust の i32
が C言語の int
と同等の型であることから、ctypes.c_int
の型を当てはめて関数を呼び出しています。
Cargo を利用してライブラリを生成する場合
Cargo でライブラリを生成する場合の手順です。
lib プロジェクトを作成する
まずは、--lib
のオプションを付けてプロジェクトを作成します。
$ cargo new add --lib
Created library `add` package
Cargo.toml に cdylib を指示する
Cargo.toml を編集し、下記の [lib]
部分を追記します。
これにより、rustc で --crate-type=cdylib
を指定するのと同じ効果が得られます。
[lib]
crate-type = ["cdylib"]
[package]
name = "add-rs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lib.rs を編集する
プロジェクトの src
ディレクトリ配下に lib.rs
が自動生成されています。
rustc の手順と同じように、add_int
関数を定義します。
#[no_mangle]
pub extern "C" fn add_int(a: i32, b: i32) -> i32 {
a + b
}
ビルドする
cargo build
のコマンドで、ライブラリを生成することができます。target/debug
ディレクトリ配下に libadd.so
が出力されます。
このファイルをコピーして利用すれば、main.py
から add_int
を呼び出すことができます。
$ cargo build
Compiling add v0.1.0 (/home/gari/sandbox/add)
Finished dev [unoptimized + debuginfo] target(s) in 0.40s
$ ls target/debug/libadd.so
target/debug/libadd.so
なお、cargo build --release
としてビルドした場合は、target/release
配下にライブラリが出力されます。
コメント