当将字符串传递给 Rust WASM 模块时,传递的数据显示为空白,根据模式匹配real_code::compute
功能
以下代码是我尝试过的。我不知道这是否与它的返回方式有关,但是当我传递硬编码时&str
,效果很好。但是,那JsInteropString
显示为空白。
以下是我在将字符串发送到 WASM 之前对其进行编码的方式(来自将 JavaScript 字符串传递给编译为 WebAssembly 的 Rust 函数 https://stackoverflow.com/questions/49014610/passing-a-javascript-string-to-a-rust-function-compiled-to-webassembly)
const memory = new WebAssembly.Memory({ initial: 20,
maximum: 100 });
const importObject = {
env: { memory }
};
const memoryManager = (memory) => {
var base = 0;
// NULL is conventionally at address 0, so we "use up" the first 4
// bytes of address space to make our lives a bit simpler.
base += 4;
return {
encodeString: (jsString) => {
// Convert the JS String to UTF-8 data
const encoder = new TextEncoder();
const encodedString = encoder.encode(jsString);
// Organize memory with space for the JsInteropString at the
// beginning, followed by the UTF-8 string bytes.
const asU32 = new Uint32Array(memory.buffer, base, 2);
const asBytes = new Uint8Array(memory.buffer, asU32.byteOffset + asU32.byteLength, encodedString.length);
// Copy the UTF-8 into the WASM memory.
asBytes.set(encodedString);
// Assign the data pointer and length values.
asU32[0] = asBytes.byteOffset;
asU32[1] = asBytes.length;
// Update our memory allocator base address for the next call
const originalBase = base;
base += asBytes.byteOffset + asBytes.byteLength;
return originalBase;
}
};
};
像这样调用 wasm:
//...loading and compiling WASM, getting instance from promise (standard)
const testStr = "TEST"
const input = myMemory.encodeString(testStr);
const offset = instance.exports.func_to_call(input);
// A struct with a known memory layout that we can pass string information in
#[repr(C)]
pub struct JsInteropString {
data: *const u8,
len: usize,
}
// Our FFI shim function
#[no_mangle]
pub unsafe extern "C" fn func_to_call(s: *const JsInteropString) -> *mut c_char {
// ... check for nulls etc
/*
THROWS ERROR
error[E0609]: no field `data` on type `*const JsInteropString`
*/
//////let data = std::slice::from_raw_parts(s.data, s.len);
//this fixes the above error
let data = std::slice::from_raw_parts((*s).data, (*s).len);
let dataToPrint: Result<_, _> = std::str::from_utf8(data);
let real_res: &str = match dataToPrint {
Ok(s) => real_code::compute(dataToPrint.unwrap()), //IS BLANK
//Ok(s) => real_code::compute("SUCCESS"), //RETURNS "SUCCESS made it"
Err(_) => real_code::compute("ERROR"),
};
unsafe {
let s = CString::new(real_res).unwrap();
println!("result: {:?}", &s);
s.into_raw()
}
}
mod real_code {
pub fn compute(operator: &str) -> &str {
match operator {
"SUCCESS" => "SUCCESS made it",
"ERROR" => "ERROR made it",
"TEST" => "TEST made it",
_ => operator,
}
}
}
当从 JavaScript 调用该函数时,它应该返回传递的相同字符串。我什至用 rustup 更新了我的 Rust 环境……有什么想法吗?
UPDATE
使用参考帖子的更具代表性的版本:
#[no_mangle]
pub unsafe extern "C" fn compute(s: *const JsInteropString) -> i32 {
let s = match s.as_ref() {
Some(s) => s,
None => return -1,
};
// Convert the pointer and length to a `&[u8]`.
let data = std::slice::from_raw_parts(s.data, s.len);
// Convert the `&[u8]` to a `&str`
match std::str::from_utf8(data) {
Ok(s) => real_code::compute(s),
Err(_) => -2,
}
}
mod real_code {
pub fn compute(operator: &str) -> i32 {
match operator {
"SUCCESS" => 1,
"ERROR" => 2,
"TEST" => 3,
_ => 10,
}
}
}
无论通过js编码传递的字符串如何,它仍然从compute返回默认值。不知道引用的帖子是否真正解决了问题。
所以是的,代码可以编译。这是我试图解决的运行时问题。字符串的编码方式和传递给 WASM 的方式有些问题。
Update 2
尝试将我的工具链切换到 Nightly,我还发现以下宏/属性会抛出有关不稳定属性的错误
#![feature(wasm_import_memory)]
#![wasm_import_memory]
在意识到我需要告诉 Rust 将导入内存后,这似乎解决了字符串作为空传递的问题。它不是空的,看起来甚至没有通过。
.cargo 文件内部
[target.wasm32-unknown-unknown]
rustflags = [
"-Clink-args=-s EXPORTED_FUNCTIONS=['_func_to_call'] -s ASSERTIONS=1",
"-C", "link-args=--import-memory",
]
这似乎已经成功了! @Shepmaster,为其他偶然发现它的人更新去年的答案,因为它具有良好的搜索引擎优化。这是由于生锈环境的变化造成的。