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’s i32.
  • 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 a Point in Rust with x = 10 and y = 20.
    • RustBindings.getX.invokeExact(point) retrieves the x value from the Rust point.
    • RustBindings.freePoint.invokeExact(point) frees the memory in Rust.