概要
JNAを使ってRustを呼び出す方法をまとめました。
前準備
まず、JNAのライブラリをmavenなりbuild.sbtなりに追加します。
build.sbt
scalaVersion := "2.13.6"
libraryDependencies += "net.java.dev.jna" % "jna" % "5.9.0"
libraryDependencies += "net.java.dev.jna" % "jna-platform" % "5.9.0"
JavaからRustを呼び出す
Hello World的なプログラム
Rustで共有ライブラリを作る
以下のようなプログラムを作成します。
main.rs
// #[no_mangle]を指定すると、コンパイル時にメソッド名を変更されないようすることができます。
#[no_mangle]
fn main() {
println!("Hello, world!");
}
そして、(macの場合)以下のコマンドで共有ライブラリを作成します。
rustc main.rs --crate-type=dylib
※ 本当はcargoでビルドした方が実践的でよさそうですね
Java側の実装
JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;
public class JnaHoge {
public interface HogeInterface extends Library {
HogeInterface INSTANCE = Native.load("main", HogeInterface.class);
void main();
}
public static void main(String[] args) {
System.out.println("started");
HogeInterface.INSTANCE.main();
System.out.println("finished");
}
}
ディレクトリツリー
以下はmain.rs
, libmain.dylib
, JnaHoge.java
のディレクトリ配置です。
.
├── build.sbt
├── libmain.dylib
├── main.rs
├── project
│ ├── build.properties
|
├── src
│ ├── main
│ │ ├── java
│ │ │ └── JnaHoge.java
│ │ ├── resources
│ │ └── scala
│ └── test
│ └── scala
実行結果
sbtの場合、sbt "runMain JnaHoge"で実行できます。
実行結果:
started
Hello, world!
finished
Rustで共有ライブラリを作って、Javaから呼び出すサンプルコード
Rustのstd::os::raw
モジュールで定義されている型は、Cの特定の型と同じ表現になることが保証されています。
従って、Rustで他言語から呼び出されるような関数を作成する際は、引数と戻り値でstd::os::raw
の型を使用することになりそうです。
int型を渡してint型の値を得る
main.rs
use std::os::raw::c_int;
#[no_mangle]
fn plus_one(x: c_int) -> c_int {
x + 1
}
JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;
public class JnaHoge {
public interface HogeInterface extends Library {
HogeInterface INSTANCE = Native.load("main", HogeInterface.class);
int plus_one(int x);
}
public static void main(String[] args) {
System.out.println("started");
int x = HogeInterface.INSTANCE.plus_one(1);
System.out.println(x);
System.out.println("finished");
}
}
実行結果:
started
2
finished
String型を渡してString型の値を得る
main.rs
use std::os::raw::c_char;
use std::ffi::CString;
#[no_mangle]
unsafe fn append_hoge(cs: *mut c_char) -> *mut c_char {
let mut cs_string = CString::from_raw(cs).into_string().expect("Failed to create String");
cs_string.push_str("hoge");
CString::new(cs_string).expect("Failed to create CString").into_raw()
}
JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;
public class JnaHoge {
public interface HogeInterface extends Library {
HogeInterface INSTANCE = Native.load("main", HogeInterface.class);
String append_hoge(String str);
}
public static void main(String[] args) {
System.out.println("started");
String str = HogeInterface.INSTANCE.append_hoge("ほげ_");
System.out.println(str);
System.out.println("finished");
}
}
実行結果:
started
ほげ_hoge
finished
配列を渡して配列を得る
main.rs
use std::os::raw::c_int;
#[no_mangle]
unsafe fn add_one(array: *mut c_int, new_array: *mut c_int) {
let new_slice = std::slice::from_raw_parts_mut(new_array, 3);
let slice = std::slice::from_raw_parts(array, 3);
new_slice[0] = slice[0] * 10;
new_slice[1] = slice[1] * 10;
new_slice[2] = slice[2] * 10;
}
JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;
public class JnaHoge {
public interface HogeInterface extends Library {
HogeInterface INSTANCE = Native.load("main", HogeInterface.class);
void add_one(int[] array, int[] newArray);
}
public static void main(String[] args) {
System.out.println("started");
int[] array = {4, 5, 6};
int[] newArray = new int[3];
HogeInterface.INSTANCE.add_one(array, newArray);
System.out.println(newArray[0]);
System.out.println(newArray[1]);
System.out.println(newArray[2]);
System.out.println("finished");
}
}
実行結果:
started
40
50
60
finished
構造体を渡してJavaのクラスを得る
main.rs
use std::os::raw::c_int;
use std::os::raw::c_char;
#[repr(C)] // C言語の構造体と互換性を持たせるようにするアトリビュート
struct Person {
age: c_int,
name: *mut c_char
}
#[no_mangle]
unsafe fn set_age(person: *mut Person) -> *mut Person {
(*person).age = 25;
person
}
JnaHoge.java
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
public class JnaHoge {
public interface HogeInterface extends Library {
HogeInterface INSTANCE = Native.load("main", HogeInterface.class);
@Structure.FieldOrder({"age", "name"})
class Person extends Structure {
public int age;
public String name;
void setName(String str) {
name = str;
}
}
Person set_age(Person person);
}
public static void main(String[] args) {
System.out.println("started");
HogeInterface.Person person = new HogeInterface.Person();
person.setName("パターソン");
HogeInterface.Person newPerson = HogeInterface.INSTANCE.set_age(person);
System.out.println(person.name + "(" + person.age + ")");
System.out.println(newPerson.name + "(" + newPerson.age + ")");
System.out.println("finished");
}
}
実行結果:
started
パターソン(25)
パターソン(25)
finished