inline java
Embed Java directly in Rust — evaluated at program runtime (java!, java_fn!) or at
compile time (ct_java!).
Prerequisites
Java 8+ with javac and java on PATH.
Quick start
# Cargo.toml
[]
= "0.1.0"
java! — runtime, no parameters
Compiles and runs Java each time the surrounding Rust code executes. Expands
to Result<T, inline_java::JavaError>.
use java;
// No type annotation needed — the macro infers `i32` from `static int run()`
let x = java! .unwrap;
java_fn! — runtime, with parameters
Like java!, but run(...) may declare parameters. Expands to a Rust
function value fn(P1, P2, …) -> Result<T, JavaError>. Parameters are
serialised by Rust and piped to the Java process over stdin.
use java_fn;
// Single parameter — return type inferred from `static int run()`
let doubled = java_fn! .unwrap;
// Multiple parameters
let msg: String = java_fn! .unwrap;
// Optional parameter
let result: = java_fn! .unwrap;
ct_java! — compile time
Runs Java during rustc macro expansion and splices the result as a Rust
literal at the call site. No parameters are allowed (values must be
compile-time constants).
use ct_java;
const PI: f64 = ct_java! ;
// Arrays work too — result is a Rust array literal baked into the binary
const PRIMES: = ct_java! ;
Supported parameter types (java_fn!)
Declare parameters in the Java run(...) signature; Rust receives them with
the mapped types below.
| Java parameter type | Rust parameter type |
|---|---|
byte |
i8 |
short |
i16 |
int |
i32 |
long |
i64 |
float |
f32 |
double |
f64 |
boolean |
bool |
char |
char |
String |
&str |
T[] / List<BoxedT> |
&[T] |
Optional<BoxedT> |
Option<T> |
Supported return types
| Java return type | Rust return type |
|---|---|
byte |
i8 |
short |
i16 |
int |
i32 |
long |
i64 |
float |
f32 |
double |
f64 |
boolean |
bool |
char |
char |
String |
String |
T[] / List<BoxedT> |
Vec<T> |
Optional<BoxedT> |
Option<T> |
Types can be nested arbitrarily: Optional<List<Integer>> → Option<Vec<i32>>,
List<String[]> → Vec<Vec<String>>, etc.
Options
The following optional key = "value" pairs may appear before the Java body, separated by
commas:
javac = "<args>"— extra arguments forjavac(shell-quoted).java = "<args>"— extra arguments forjava(shell-quoted).
use java;
let result: String = java! .unwrap;
Cache directory
Compiled .class files are cached so that unchanged Java code is not
recompiled on every run. The cache root is resolved in this order:
| Priority | Location |
|---|---|
| 1 | INLINE_JAVA_CACHE_DIR environment variable (if set and non-empty) |
| 2 | Platform cache directory — ~/.cache/inline_java on Linux, ~/Library/Caches/inline_java on macOS, %LOCALAPPDATA%\inline_java on Windows |
| 3 | <system temp>/inline_java (fallback if the platform cache dir is unavailable) |
Each compiled class gets its own subdirectory named
<ClassName>_<hash>/, where the hash covers the Java source, the
expanded javac flags, the current working directory, and the raw java
flags. This means changing any of those inputs automatically triggers a
fresh compilation.
Using project Java source files
Use import or package directives together with javac = "-sourcepath <path>"
(or -classpath) to call into your own Java code:
use java;
// import style
let s: String = java! .unwrap;
// package style — the generated class becomes part of the named package
let s: String = java! .unwrap;
Refactoring use case
inline_java is particularly well-suited for incremental Java → Rust
migrations. The typical workflow is:
- Keep the original Java logic intact.
- Write the replacement in Rust.
- Use
java_fn!to call the original Java with the same inputs and assert that both implementations produce identical outputs.
use java_fn;
parity_with_java;
Crate layout
| Crate | Purpose |
|---|---|
inline_java |
Public API — re-exports macros and core types |
inline_java_macros |
Proc-macro implementation (java!, java_fn!, ct_java!) |
inline_java_core |
Runtime helpers (run_java, JavaError) |
inline_java_demo |
Demo binary |