https://github.com/mozilla/uniffi-rs
You can generate bindings for multiple languages. It supports error handling on both sides and latest versions also support native async integration.
I've used it to reuse the same Rust engine in iOS and Android apps and write native UI.
Bindings were easy, everything else (building, linking,...) was a bit pain to setup because there no good examples.
At some point, repeated calls into the JNI are counter-productive to performance, since the JIT can not optimize them. Pinning affecting garbage collection is another potential drawback if any of your rust calls are long lived. If we don't measure and just conclude "we are fast because rust is faster than Java, and we took average of both speeds", it's a disservice.
2. Also, I see unsafe for each call? I'd rather isolate this into a class / different file, since in JNI only few types of calls are possible. (method returning one of the primtive types, an object or `void`). This is the approach I took in dart jnigen. (Though there, the call is Dart -> Java, not Java -> Native language).
unsafe {
env.call_method_unchecked(
java_logger,
logger_method,
ReturnType::Primitive(Primitive::Void),
&[JValue::from(format_msg(record)).as_jni()]
);
}
3. I believe some details are missing here. What's native_add_one mapped to? And how is tokio futures awaited from Java? I believe that's the important part you should be presenting. public CompletableFuture<Integer> add_one(int x) {
long futureId = native_add_one(x); // Call Rust
return AsyncRegistry.take(futureId); // Get CompletableFuture
}
4. Also please don't use ChatGPT for writing anything. It totally derails the reader by mentioning irrelevant details and long winded corporate conclusion at the end of every sentence.1. I agree that using Rust doesn't necessarily mean faster performance; it simply gives you the opportunity to implement some compute-intensive modules in Rust, which is a possible approach.
2. This is a great suggestion, and we organized our project in the same way. You don’t need to use unsafe for every call. However, if you want to call JNI APIs from Rust, unsafe is required.
3. Sorry, some details were missing here. We use AsyncRegistry(Java) as an intermediary. Before initiating an async operation in Rust, we need to call Java code in advance to register a future and obtain a unique future ID. After the async execution completes, we retrieve the registered future by its ID, and then complete it or complete it exceptionally depending on the async result. You can refer to this code: https://github.com/GreptimeTeam/rust-java-demo/blob/90ffa0ba... and https://github.com/GreptimeTeam/rust-java-demo/blob/90ffa0ba...
4. This article was not generated by AI; it’s just that our official blog has a fixed template at the end. Sorry for the inconvenience.
5. How did you handle java local and global ref lifetimes in rust callee? Was it assumed that java caller owns all the refs and freed after the rust computation returns? Or did your calls mostly involve byte buffers and primitive types? That latter is a sweet spot but not always feasible.
JNI introduces a lot of other challenges and sacrifice in terms of portability, memory and retaining observability and control within the JVM.
Chicory even supports AOT compilation to translate wasm bytecode to JVM bytecode for native performance.
> It is not a goal to deprecate JNI or to remove JNI from the Java Platform.
It says they want to put a safety barrier in front of JNI, so you'll have to explicitly indicate that you want a module to use JNI.
That's one of the reasons I posted it. A lot of knowledgeable people here can chime in more details. And a sibling comment did!
But does rust really give you an edge over C/C++?
Here is how you do JNI with C++: http://move.rupy.se/file/jvm.txt
So simple it's ridiculous!
Then you can use RegisterNatives to give C++ API to the Java side instead of the stub (Java calls C++ .dll/.so) thing...
That is the systems language most JVM implementations make use of, alongside Java, and what is directly supported by JNI tooling, including on Java IDEs mixed language debugging.
And in what concerns Java, native is anyway the synonym for unsafe.
However to each their own.
I am well aware of the issues with C++'s flaws, sometimes it is easier to deal with such flaws, than adding a new layer into the C++ sandwich of debugging tools, build toolchain and IDE tooling for a given language runtime, and dragging a whole team for the ride as well.
What about removing LLVM dependency from Rust, switch to Cranelift, so that rustc isn't hindered by C++ mistakes on LLVM?
For me the big advantage of Cranelift isn't that it's written in Rust it's that they seem to have invested more into coherent semantics. I do not want to write code which is correct but is miscompiled because the compiler internal semantics are nonsense and that's a small but noticeable problem in LLVM.
You can write contorted (but safe) Rust which LLVM just plainly miscompiles, the rust layer is like "Make local variable A, and local variable B" Ok says LLVM, "and now is the address of A the same as the address of B?" "No", says LLVM those are different, not the same variable so different addresses. OK says the Rust. Now, subtract the smaller from the larger and tell me the number you got. "Zero" triumphantly proclaims LLVM having deduced that we don't need A or B so there's no need to store them anywhere, but forgotten that it promised their addresses aren't the same... Oops.
That example is silly, but it's hard to be sure how many large codebases might tickle equivalent LLVM miscompilation, which is not good.
You can use Zig instead if it meets your needs, but last time I checked, the Zig ecosystem was lacking compared to Rust.
> or using Deno instead, if Rust as extension language
Deno? There was a thread the other day that Deno is dead. I wouldn't even compare Deno and Bun because Bun is just waaay better. Even before Bun already felt very polished and nice to use, and now it even has native postgres and s3 clients. And it doesn't try to sell you a KV database or some shit.