Xross (Cross) is a high-performance, memory-safe cross-language framework designed to dissolve the boundary between Rust and JVM (Kotlin/Java).
By leveraging Project Panama (Foreign Function & Memory API), which is standardized in Java 25, it fundamentally resolves the performance limits and development complexity inherent in traditional JNI (Java Native Interface).
- β‘οΈ Extreme Performance: Eliminates JNI-specific overhead via
MethodHandleand inline memory access. Minimizes the cost of native calls. - π‘οΈ Rust Safety for JVM: Extracts Rust's ownership model (Owned, Ref, MutRef) as metadata and integrates it into Kotlin's lifecycle management and type system.
- π οΈ Fully Automated Bindings: Simply annotate your Rust code, and thread-safe, idiomatic Kotlin code is automatically generated.
- π Robust Thread Safety: Automatically selects synchronization mechanisms such as
StampedLock,VarHandle, orAtomicbased on data nature to prevent data races. - π Async/Await Integration: Seamlessly bridges Rust's
Futureand Kotlin'sCoroutines. Native async logic can be called assuspendfunctions. - π Advanced Type Support: Handles structs, Rust-specific enums (Algebraic Data Types), opaque types, and slices (
&[T],&mut [T]) seamlessly.
Xross consists of the following components. See ARCHITECTURE.md for detailed specifications.
xross-core: Rust-side runtime foundation and annotations.xross-macros: Procedural macros for proxy code generation.xross-plugin: A powerful Gradle plugin that generates Kotlin bindings.xross-metadata: High-precision type definition schema shared across languages.
You can introduce the plugin in several ways depending on your project.
Best for trying out the latest version during development or contributing to Xross itself.
- Clone the
xrossrepository. - Add the following to your project's
settings.gradle.kts:pluginManagement { includeBuild("../path/to/xross/xross-plugin") } - Apply it in
build.gradle.kts:plugins { id("org.xross") }
Use the main branch directly from GitHub without cloning.
// settings.gradle.kts
pluginManagement {
repositories {
maven { url = uri("https://jitpack.io") }
gradlePluginPortal()
}
}
// build.gradle.kts
buildscript {
repositories { maven { url = uri("https://jitpack.io") } }
dependencies { classpath("com.github.the-infinitys:xross:3.2.0") }
}
apply(plugin = "org.xross")[dependencies]
xross-core = "3.2.0"Xross analyzes Rust type definitions and generates optimal Kotlin code.
| Rust Code | Generated Kotlin Code | Characteristics |
|---|---|---|
#[derive(XrossClass)] struct S |
class S : AutoCloseable |
Class managing native memory |
#[xross_new] fn new() -> Self |
constructor(...) |
Creates a Rust instance |
&self / &mut self |
Ordinary methods | Thread safety automatically applied |
async fn foo() |
suspend fun foo() |
Async function integrated with Coroutines |
self (Ownership consumption) |
fun consume()... |
Invalidated on Kotlin side after call |
Option<T> |
T? (Nullable) |
Natural expression using null |
Result<T, E> |
Result<T> |
Standard Result type containing exceptions |
Rust:
#[xross_class]
impl MyService {
#[xross_method]
pub async fn process(&self, input: String) -> Result<String, String> {
tokio::time::sleep(Duration::from_millis(10)).await;
Ok(format!("Processed: {}", input))
}
}Kotlin (Generated):
val result: Result<String> = runBlocking {
service.process("hello")
}Xross automatically generates extern "C" functions internally and calls them via Java 25's FFM API (MethodHandle).
Generated symbols follow this format:
{crate}_{package}_{type}_{method}
Example: my_lib_com_example_MyService_process
For every XrossClass, the following management functions are generated:
_drop: CallsBox::from_rawto release Rust-side memory._size: Returnssize_ofof the type, used forMemorySegmentallocation on the Kotlin side._clone: IfCloneis implemented, creates a new instance on the heap.
If you want to call specific functions directly via the FFM API, you can interoperate with Xross-managed objects by performing a SymbolLookup following these naming conventions.
use xross_core::{XrossClass, xross_class};
#[derive(XrossClass)]
pub struct Calculator {
#[xross_field(safety = Atomic)]
pub value: i32,
}
#[xross_class]
impl Calculator {
#[xross_new]
pub fn new(value: i32) -> Self {
Self { value }
}
#[xross_method]
pub fn add(&mut self, amount: i32) {
self.value += amount;
}
}./gradlew generateXrossBindingsimport com.example.generated.Calculator
fun main() {
// Safely create a Rust instance (automatically released via AutoCloseable)
Calculator(10).use { calc ->
// Atomic update
calc.value.update { it + 5 }
println("Result: ${calc.value.value}") // 15
}
}Rust async fn is generated as a suspend function on the Kotlin side. Internally, it builds an efficient bridge that polls the Rust Future and resumes the Coroutine upon completion.
Xross brings Rust's borrow checker concepts to Kotlin.
- Atomic: Provides CAS operations via
VarHandle. - Lock: Uses
StampedLockto apply optimistic reads for immutable references and exclusive locks for mutable references.
Rust enum is generated as a Kotlin sealed class, allowing safe pattern matching via when expressions.
Using #[xross_function], you can bind global functions that do not belong to a class.
Using #[xross_core::opaque_class], you can safely pass pointers to Kotlin while hiding Rust-side details.
For cases where the default conversion logic is insufficient, you can use #[xross_raw_method] and #[xross_raw_function] to manually define the FFI boundary.
- Custom Signatures: Manually specify FFI-compatible types (
sig). - Manual Conversion: Define
importandexportclosures to bridge FFI types and Rust types. - Optimization: Bypass high-level abstractions for extreme performance.
- Panic Safety: Use
panicableto safely catch Rust panics at the FFI boundary.
Example:
#[xross_raw_method {
sig = (p: Point) -> Point;
import = |p| { p };
export = |res| { res };
}]
pub fn move_point_raw(&self, mut p: Point) -> Point {
p.x += 10;
p.y += 20;
p
}- Ownership Awareness: Objects returned as
Ownedmust be released using auseblock or by callingclose(). - Package Management: Use
#[xross_package("com.example")]to organize your Kotlin package structure. - Error Handling: Rust's
Result<T, E>is converted to Kotlin'sResult<T>. Handle exceptions appropriately usingonFailure, etc. - Minimize Allocation: Frequently allocating/releasing memory on the native side may be slower than JVM's memory management (TLAB) in some cases.
- Be Cache-Aware: Flatten data and use memory access patterns that are easy for the CPU to prefetch to realize the true value of Native.
- Heavier Processing per Call: If the execution time on the Rust side is long enough, the overhead of the FFI boundary becomes negligible.
- Rust: 1.80+ (Edition 2024 recommended)
- Java: 25+ (Project Panama / FFM API)
- Gradle: 8.0+
At runtime, the following JVM argument is required to permit FFM API access:
--enable-native-access=ALL-UNNAMEDThis project is licensed under the MIT License.