Setting Up Java
Once the Rust library is compiled, Java can load the shared library and access the Rust functions.
Step 1: Loading the Rust Shared Library
Java uses SymbolLookup
to load the shared library and retrieve the addresses of the Rust functions. Java’s Linker
allows us to bind those addresses to callable MethodHandle
objects, which represent native functions in Java.
Here’s how to load the Rust library and link the create_point
function:
Java Example
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class RustBindings {
static MethodHandle createPoint;
static MethodHandle getX;
static MethodHandle freePoint;
static {
var linker = Linker.nativeLinker(); // Initializes the native linker
var lib = SymbolLookup.libraryLookup("libmyrustlib.so", Arena.global()); // Loads the Rust library
// Link the Rust functions
createPoint = linker.downcallHandle(
lib.find("create_point").orElseThrow(),
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)
);
getX = linker.downcallHandle(
lib.find("get_x").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
);
freePoint = linker.downcallHandle(
lib.find("free_point").orElseThrow(),
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)
);
}
}
Explanation:
libraryLookup
: Loads the Rust shared library (libmyrustlib.so
). The library must be available in the Java classpath or in the system’s library path.FunctionDescriptor
: Defines the signature of the Rust function in Java terms. For example:ValueLayout
.ADDRESS
: Corresponds to a pointer (Rust’s*mut
).ValueLayout.JAVA_INT
: Corresponds to Rust’si32
.
- MethodHandle: Represents the linked Rust function. This is how Java will call the Rust function.
Step 2: Calling Rust Functions from Java
With the library loaded and the functions linked, we can now call the Rust functions from Java using MethodHandle.invokeExact()
. Here’s how to create a point in Rust, get its x
value, and free the memory:
Java Example
public class Main {
public static void main(String[] args) throws Throwable {
// Create a point in Rust
MemorySegment point = (MemorySegment) RustBindings.createPoint.invokeExact(10, 20);
// Get the x value from the point
int xValue = (int) RustBindings.getX.invokeExact(point);
System.out.println("X value: " + xValue);
// Free the Rust point
RustBindings.freePoint.invokeExact(point);
}
}
Explanation:
MemorySegment
: This is Java’s way of handling memory passed to and from Rust. Here, it represents the raw pointer to the Rust Point structure.invokeExact()
: Calls the linked Rust function with the specified arguments. In this case:RustBindings.createPoint.invokeExact(10, 20)
creates aPoint
in Rust withx = 10
andy = 20
.RustBindings.getX.invokeExact(point)
retrieves thex
value from the Rust point.RustBindings.freePoint.invokeExact(point)
frees the memory in Rust.