JavaからRustを呼び出す

# 概要

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`の型を使用することになりそうです。

https://doc.rust-lang.org/std/os/raw/index.html

### 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
```