読者です 読者をやめる 読者になる 読者になる

hidekatsu-izuno 日々の記録

プログラミング、経済政策など伊津野英克が興味あることについて適当に語ります(旧サイト:A.R.N [日記])

Rust 言語で JNI ライブラリを作る

プログラミング

Java にはプラットフォーム固有のライブラリを利用するために JNI (Java Native Interface) という機構が用意されている。この機構の問題点は、どこのプラットフォームにバイナリを持っていっても同じように動くという Java の最大の利点を失わせてしまうことにもちろんある。それはそうなのだけれど、最大の問題は様々な環境用のバイナリを用意するにはそれぞれコンパイル環境を用意する必要があり、現実的には難しいという点にある。

そこで考えたのだが、Rust はクロスコンパイルができるようになったと聞いた覚えがある。ということは単一環境(例えば、普段デスクトップとして使っているWindows PC)からでも各環境に対応した共有ライブラリが生成できるのではなかろうか。

まずは、Rust で JNI ができるのか調べてみた。すると出てきたのが次のサイト。

nitschinger.at

問題なくできるようだ。さらに調べたところ、

github.com

を使えば、jni.h の定義を Rust 用にいちいち手動変換する必要もないこともわかった。

Rust 付属のパッケージマネージャーは npm と似た仕組みになっているので、簡単にライブラリを追加できる。Cargo.toml に次の記述を追加する。

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

[dependencies]
jni-sys = "0.1"

はじめは、crate-type = ["dylib"] と指定していたのだけれど、JNIで呼び出す場合は、Rust からの呼び出しは必要ないので、外部呼び出し専用の cdylib で構わない。これを指定するだけでファイルサイズが 2.2MB から 22KB になる。

さて、前述のサイトの add メソッドを jni-sys を使用して書き直すとこうなるになる。

extern crate jni_sys;

use jni_sys::*;

#[no_mangle]
#[allow(unused_variables, non_snake_case)]
pub extern fn Java_RustTest_add(jre: *mut JNIEnv, class: jclass, v1: jint, v2: jint) -> jint {
    v1 + v2
}

あとは普通に cargo build --release するだけでいい。予想以上に簡単。

この記述を見ればわかるように、プラットフォームに依存する記述はないため、この時点で少なくともマルチプラットフォームに対応できていることになる。

あとは、クロスコンパイル……と行きたいところなのだけれど、残念ながら現時点ではうまく行っていない。どうもランタイム環境は必要となるらしく、Windows 上で Linux バイナリを作るのは難しいようだ(Windows Subsystem for Linux を使えばできるはずだけれども)。 とほほ。

当初の思惑とは外れたとはいえ、予想外に簡単に JNI が実現できたことがわかったのは良かった。あとは SQLite とか各種ライブラリが Rust に移植されてくるといろんなことができるようになって面白くなるのだけど、そういう未来は来るのだろうか。