29 releases

0.11.0 Feb 21, 2019
0.10.2 Sep 11, 2018
0.10.1 Apr 23, 2018
0.9.3 Jan 12, 2018
0.2.0 Nov 29, 2016

#13 in FFI

Download history 936/week @ 2018-11-08 943/week @ 2018-11-15 1056/week @ 2018-11-22 686/week @ 2018-11-29 593/week @ 2018-12-06 599/week @ 2018-12-13 364/week @ 2018-12-20 226/week @ 2018-12-27 388/week @ 2019-01-03 452/week @ 2019-01-10 356/week @ 2019-01-17 312/week @ 2019-01-24 337/week @ 2019-01-31 286/week @ 2019-02-07 317/week @ 2019-02-14

1,870 downloads per month
Used in 10 crates (7 directly)



Build Status Docs Crates.io

JNI bindings for Rust

Join the chat at https://gitter.im/jni-rs/Lobby

See the docs for more details.


cd example


See the Contribution Guide for details.


Licensed under either of


Safe JNI Bindings in Rust

This crate provides a (mostly) safe way to implement methods in Java using the JNI. Because who wants to actually write Java?

Getting Started

Naturally, any ffi-related project is going to require some code in both languages that we're trying to make communicate. Java requires all native methods to adhere to the Java Native Interface (JNI), so we first have to define our function signature from Java, and then we can write Rust that will adhere to it.

The Java side

First, you need a Java class definition. HelloWorld.java:

class HelloWorld {
    // This declares that the static `hello` method will be provided
    // a native library.
    private static native String hello(String input);

    static {
        // This actually loads the shared object that we'll be creating.
        // The actual location of the .so or .dll may differ based on your
        // platform.

    // The rest is just regular ol' Java!
    public static void main(String[] args) {
        String output = HelloWorld.hello("josh");

Compile this to a class file with javac HelloWorld.java.

Trying to run it now will give us the error Exception in thread "main" java.lang.UnsatisfiedLinkError: no mylib in java.library.path since we haven't written our native code yet.

To do that, first we need the name and type signature that our Rust function needs to adhere to. Luckily, the Java compiler can generate that for you! Run javac -h . HelloWorld and you'll get a HelloWorld.h output to your directory. It should look something like this:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
 * Class:     HelloWorld
 * Method:    hello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
JNIEXPORT jstring JNICALL Java_HelloWorld_hello
  (JNIEnv *, jclass, jstring);

#ifdef __cplusplus

It's a C header, but luckily for us, the types will mostly match up. Let's make our crate that's going to compile to our native library.

The Rust side

Create your crate with cargo new mylib. This will create a directory mylib that has everything needed to build an basic crate with cargo. We need to make a couple of changes to Cargo.toml before we do anything else.

  • Under [dependencies], add jni = "0.10.2"
  • Add a new [lib] section and under it, crate_type = ["cdylib"].

Now, if you run cargo build from inside the crate directory, you should see a libmylib.so (if you're on linux/OSX) in the target/debug directory.

The last thing we need to do is to define our exported method. Add this to your crate's src/lib.rs:

extern crate jni;

// This is the interface to the JVM that we'll call the majority of our
// methods on.
use jni::JNIEnv;

// These objects are what you should use as arguments to your native
// function. They carry extra lifetime information to prevent them escaping
// this context and getting used after being GC'd.
use jni::objects::{JClass, JString};

// This is just a pointer. We'll be returning it from our function. We
// can't return one of the objects with lifetime information because the
// lifetime checker won't let us.
use jni::sys::jstring;

// This keeps Rust from "mangling" the name and making it unique for this
// crate.
// This turns off linter warnings because the name doesn't conform to
// conventions.
pub extern "system" fn Java_HelloWorld_hello(env: JNIEnv,
// This is the class that owns our static method. It's not going to be used,
// but still must be present to match the expected signature of a static
// native method.
                                             class: JClass,
                                             input: JString)
                                             -> jstring {
    // First, we have to get the string out of Java. Check out the `strings`
    // module for more info on how this works.
    let input: String =
        env.get_string(input).expect("Couldn't get java string!").into();

    // Then we have to create a new Java string to return. Again, more info
    // in the `strings` module.
    let output = env.new_string(format!("Hello, {}!", input))
        .expect("Couldn't create java string!");

    // Finally, extract the raw pointer to return.

Note that the type signature for our function is almost identical to the one from the generated header, aside from our lifetime-carrying arguments.

Final steps

That's it! Build your crate and try to run your Java class again.

... Same error as before you say? Well that's because JVM is looking for mylib in all the wrong places. This will differ by platform thanks to different linker/loader semantics, but on Linux, you can simply export LD_LIBRARY_PATH=/path/to/mylib/target/debug. Now, you should get the expected output Hello, josh! from your Java class.

Launching JVM from Rust

If you need to use the part of the Invocation API that allows to launch a JVM from a native process, you must enable invocation feature. The application will require linking to the dynamic jvm library, which is distributed with the JVM.

During build time, the JVM installation path is determined:

  1. By JAVA_HOME environment variable, if it is set.
  2. Otherwise — from java output.

It is recommended to set JAVA_HOME to have reproducible builds, especially, in case of multiple VMs installed.

At application run time, you must specify the path to the jvm library so that the loader can locate it.

  • On Windows, append the path to jvm.dll to PATH environment variable.
  • On MacOS, append the path to libjvm.dylib to LD_LIBRARY_PATH environment variable.
  • On Linux, append the path to libjvm.so to LD_LIBRARY_PATH environment variable.

The exact relative path to jvm library is version-specific.

For more information - see documentation in build.rs.

See Also


JNI Documentation

Open-Source Users

Other Projects Simplifying Java and Rust Communication

  • Consider JNR if you just need to use a native library with C interface
  • Watch OpenJDK Project Panama which aims to enable using native libraries with no JNI code
  • Consider GraalVM — a recently released VM that gives zero-cost interoperability between various languages (including Java and Rust compiled into LLVM-bitcode)


~22K SLoC