Here are my notes for getting a trivial Java program to call a Rust library:
- Install Java 19 Early Access (recommended: sdk install java 19.ea.29-open)
- Install jextract (it's not part of openJDK, instead look here: https://github.com/openjdk/jextract)
- Create the Rust program (TODO details, cargo init --lib)
Cargo.toml
[package] name ="myrustlibrary" version = "0.1.0" edition = "2021" [dependencies] [lib] crate_type = ["cdylib"] [build-dependencies] cbindgen = "0.20.0"
Add a build.rs file, we want to use cbindgen to create the lib.h file we'll use with jextract.
extern crate cbindgen;
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("Unable to generate bindings")
.write_to_file("lib.h");
}
Add the src/lib.rs contents, for simplicity we'll just echo the PID
use std::process;
#[no_mangle]
pub extern "C" fn rust_get_pid() -> u32 {
return process::id();
}
Now build it: cargo build
Important, keep track of where Cargo built your lib. 'lib.h' will be in the base folder, and the lib itself will be in the 'target/debug' folder (libmyrustlibrary.d libmyrustlibrary.dylib if you're on a Mac)
Run jextract on the lib.h file
./jextract -t org.rust -l myrustlibrary --output classes ./lib.h
Now there will be a bunch of class files in the classes/org/rust dir
Write a Java program to make use of the header file we created from rust (lib.h)
import static org.rust.lib_h.*; // notice this is the target package we specified when running jextract
public class Main {
public static void main(String[] args){
System.out.println("🦀 process id = " + rust_get_pid());
}
}
And finally, tie it all together
./java --enable-preview --source 19 --enable-native-access=ALL-UNNAMED -Djava.library.path=./target/debug -cp classes Main.java 🦀 process id = 5526