Python – ctypes で Rust の関数を呼び出す

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 配下にライブラリが出力されます。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次