I have an Android app (written in Kotlin), with the processing code running in Rust. I'm using JNI (Java Native Interface) as communication layer.
Goal
I can successfully return a bool
, but I want to extend the Rust function so it returns both a bool
and string
.
Working code (returns Boolean only)
Android (Kotlin)
On the Kotlin side I have the following function signature:
package eu.mypackage.rust
class SomeName {
@Throws(IllegalArgumentException::class)
external fun checkAnswer(answerInput: String, answerList: Array<String>): Boolean
}
Which I call with:
fun processAnswer(answerInput: String, answerList: List<String>): Int {
val isCorrect = sn.checkAnswer(answerInput, answerList.toTypedArray())
...
Rust
On the Rust side I have the followingcode that is successfully :
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
pub mod android {
extern crate jni;
// This is the interface to the JVM that we'll call the majority of our methods on.
// @See /
use self::jni::JNIEnv;
use self::jni::objects::{JClass, JString, JObjectArray};
use self::jni::sys::{jstring, jboolean, jint};
#[no_mangle] // This keeps Rust from "mangling" the name so it is unique (crate).
pub extern "system" fn Java_eu_mycompany_rust_MyPackage_checkAnswer<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
answerInput: JString<'local>,
answerList: JObjectArray<'local>,
) -> jboolean
// Keep in mind that arrays of jboolean values should only ever hold values of 0 or 1 because any other value could lead to undefined behaviour within the JVM.
// .JNIEnv.html#jboolean-elements
{
let answer_input: String = env.get_string(&answerInput).expect("Couldn't get java string!").into();
let answer_list = kotlin_list_to_rust_vec_string(&mut env, &answerList);
// pub fn answer_in_answer_list<'a>(answer_input: &String, answer_list: &'a Vec<String>) -> (bool, Option<&'a String>)
let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);
// TODO return both `is_correct` as bool and `corrected_answer` as string
return if is_correct {
1 // true
} else {
0 // false
}
}
}
Attempt
Kotlin
I changed the expected output type to: Pair<Boolean, String>
package eu.mypackage.rust
class SomeName {
@Throws(IllegalArgumentException::class)
external fun checkAnswer(answerInput: String, answerList: Array<String>): Pair<Boolean, String>
}
and the update calling code in Kotlin:
fun processAnswer(answerInput: String, answerList: List<String>): Int {
val (isCorrect, corrected_answer) = sn.checkAnswer(answerInput, answerList.toTypedArray())
...
Rust
However, on the Rust side I'm lost. I tried returning a tuple as jobject
with the following code, but it crashes my Android app with a panic_abort
and (core::result::unwrap_failed::h5cd03753bd6180f5+88)
:
let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);
// Create a Java boolean (1 for true, 0 for false)
let j_is_correct: jboolean = if is_correct { 1 } else { 0 };
// Convert the Rust string to a JNI string if exist, else empty JNI string
let j_corrected_answer = match corrected_answer {
Some(s) => env.new_string(&s).expect("Couldn't create Java string!"),
// empty String if no correction
None => env.new_string("").expect("Couldn't create Java string!")
};
// Create a Java Pair
let result_tuple = env
.new_object(
"android/util/Pair",
"(Ljava/lang/Object;Ljava/lang/Object;)V",
&[j_is_correct.into(), (&j_corrected_answer).into()]
)
.expect("Couldn't create Pair object");
result_tuple.into_raw()
I have an Android app (written in Kotlin), with the processing code running in Rust. I'm using JNI (Java Native Interface) as communication layer.
Goal
I can successfully return a bool
, but I want to extend the Rust function so it returns both a bool
and string
.
Working code (returns Boolean only)
Android (Kotlin)
On the Kotlin side I have the following function signature:
package eu.mypackage.rust
class SomeName {
@Throws(IllegalArgumentException::class)
external fun checkAnswer(answerInput: String, answerList: Array<String>): Boolean
}
Which I call with:
fun processAnswer(answerInput: String, answerList: List<String>): Int {
val isCorrect = sn.checkAnswer(answerInput, answerList.toTypedArray())
...
Rust
On the Rust side I have the followingcode that is successfully :
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
pub mod android {
extern crate jni;
// This is the interface to the JVM that we'll call the majority of our methods on.
// @See https://docs.rs/jni/latest/jni/
use self::jni::JNIEnv;
use self::jni::objects::{JClass, JString, JObjectArray};
use self::jni::sys::{jstring, jboolean, jint};
#[no_mangle] // This keeps Rust from "mangling" the name so it is unique (crate).
pub extern "system" fn Java_eu_mycompany_rust_MyPackage_checkAnswer<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
answerInput: JString<'local>,
answerList: JObjectArray<'local>,
) -> jboolean
// Keep in mind that arrays of jboolean values should only ever hold values of 0 or 1 because any other value could lead to undefined behaviour within the JVM.
// https://docs.rs/jni/latest/jni/struct.JNIEnv.html#jboolean-elements
{
let answer_input: String = env.get_string(&answerInput).expect("Couldn't get java string!").into();
let answer_list = kotlin_list_to_rust_vec_string(&mut env, &answerList);
// pub fn answer_in_answer_list<'a>(answer_input: &String, answer_list: &'a Vec<String>) -> (bool, Option<&'a String>)
let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);
// TODO return both `is_correct` as bool and `corrected_answer` as string
return if is_correct {
1 // true
} else {
0 // false
}
}
}
Attempt
Kotlin
I changed the expected output type to: Pair<Boolean, String>
package eu.mypackage.rust
class SomeName {
@Throws(IllegalArgumentException::class)
external fun checkAnswer(answerInput: String, answerList: Array<String>): Pair<Boolean, String>
}
and the update calling code in Kotlin:
fun processAnswer(answerInput: String, answerList: List<String>): Int {
val (isCorrect, corrected_answer) = sn.checkAnswer(answerInput, answerList.toTypedArray())
...
Rust
However, on the Rust side I'm lost. I tried returning a tuple as jobject
with the following code, but it crashes my Android app with a panic_abort
and (core::result::unwrap_failed::h5cd03753bd6180f5+88)
:
let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);
// Create a Java boolean (1 for true, 0 for false)
let j_is_correct: jboolean = if is_correct { 1 } else { 0 };
// Convert the Rust string to a JNI string if exist, else empty JNI string
let j_corrected_answer = match corrected_answer {
Some(s) => env.new_string(&s).expect("Couldn't create Java string!"),
// empty String if no correction
None => env.new_string("").expect("Couldn't create Java string!")
};
// Create a Java Pair
let result_tuple = env
.new_object(
"android/util/Pair",
"(Ljava/lang/Object;Ljava/lang/Object;)V",
&[j_is_correct.into(), (&j_corrected_answer).into()]
)
.expect("Couldn't create Pair object");
result_tuple.into_raw()
Share
Improve this question
asked Mar 13 at 7:17
NumesSanguisNumesSanguis
6,4028 gold badges46 silver badges81 bronze badges
1 Answer
Reset to default 2jboolean
is the JNI version of the Java primitive boolean
. Thus it is not a heir of java.lang.Object
.
The constructor of android.util.Pair
requires two object instances, which means jboolean
is not a compatible type.
On Java level modern compiler implements "autoboxing", which automatically converts between primitives and objects (like boolean
to/from Boolean
) but on JNI level you have to manually do this.