LoginSignup
7
1

More than 1 year has passed since last update.

JavaからRustを呼び出す

Last updated at Posted at 2021-12-04

概要

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でビルドした方が実践的でよさそうですね :thought_balloon:

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
7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1