After reading many resources, and doing my checks, trials and surely Q/As in this forum, I was able to get the below hello world single code that can be compiled to different platforms (WASM, Android and iOS).
Your comments/notes/suggestions about the below are welcomed
use wasm_bindgen::prelude::*;
use std::os::raw::{c_char};
use std::ffi::{CString, CStr};
#[no_mangle]
pub extern fn rust_greeting(to: *const c_char) -> *mut c_char {
let c_str = unsafe { CStr::from_ptr(to) };
let recipient = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
CString::new("Hello ".to_owned() + recipient).unwrap().into_raw()
}
#[cfg(target_arch = "wasm32")]
pub mod wasm {
use super::*;
#[wasm_bindgen]
pub fn wasm_greetings(to: &str) -> String {
// copy the &str into CSring
let c_str = CString::new(to).unwrap();
// cast the CString into c_char
let c_world: *const c_char = c_str.as_ptr() as *const c_char;
// pass the c_char into the required function
let returned = rust_greeting(c_world);
// Do required manipulation
// copy the c_char into CStr
let c_str = unsafe { CStr::from_ptr(returned) };
// cast the CStr into &str
let recipient = match c_str.to_str() {
Err(_) => "Error",
Ok(string) => string,
};
// cast the &str into String and return it
recipient.to_string()
}
}
// For Manual memory cleaning in iOS
#[cfg(target_os="ios")]
#[no_mangle]
pub mod ios {
use super::*;
pub extern fn rust_greeting_free(s: *mut c_char) {
unsafe {
if s.is_null() { return }
CString::from_raw(s)
};
}
}
/// Expose the JNI interface for android below
#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
extern crate jni;
use super::*;
use self::jni::JNIEnv;
use self::jni::objects::{JClass, JString};
use self::jni::sys::{jstring};
#[no_mangle]
pub unsafe extern fn Java_hasan_RustGreetings_greeting(env: JNIEnv, _: JClass, java_pattern: JString) -> jstring {
// Our Java companion code might pass-in "world" as a string, hence the name.
let world = rust_greeting(env.get_string(java_pattern).expect("invalid pattern string").as_ptr());
// Retake pointer so that we can use it below and allow memory to be freed when it goes out of scope.
let world_ptr = CString::from_raw(world);
let output = env.new_string(world_ptr.to_str().unwrap()).expect("Couldn't create java string!");
output.into_inner();
}
}
If you're going for idiomatic rust, I'd recommend having rust_greeting take &str and return String, then do the wrapping for retrieving and returning *mut c_char in a separate function. Maybe the "rustic" function would take Option<&str> for the case where conversion fails?
Then the wasm version doesn't have the additional overhead of converting back and forth. In general, I prefer doing this so the "common core" code is kept separate from the conversion code for various things (including C).
Only other thing is I'm not entirely sure why rust_greeting is #[no_mangle]? The android/ios/wasm functions are each individually exported so the code they call into shouldn't need to be no_mangle itself.
Besides that, it looks great! I like the idea of making something like this and this is an awesome proof-of-concept.
Thanks, actually the code I started work with was build initially for iOS, then Android and WASM had bee added, so the rust_greeting is mo_mangle to fit with iOS.
Your comment is absolutely correct, I've to re-write it as clean/version (if the term is valid here), then do the required conversion in each mod think the overall code will be cleaner.