如何使用字符串作为参数从 Go 调用 Rust 函数?
How do I call a Rust function from Go with a string as a parameter?
我一直在尝试将字符串传递给 Rust 函数(编译为 Wasm),但是据我了解,现在无法直接传递字符串,因为“str”不是“FFI 世界”(至少 Rust 编译器是这么说的):
= help: consider using `*const u8` and a length instead
所以我所做的就是将函数更改为这种形式(而不是使用简单的 &str 类型):
#[no_mangle]
pub extern "C" fn greet(s: *mut u8, len: usize) {
let s = std::str::from_utf8(unsafe { std::slice::from_raw_parts(s, len) }).unwrap();
println!("Hello, {}!", s)
}
这意味着我需要一个指针和u8中字符串的长度。
但是,有人让我注意到 WASM 模块是沙箱化的,因此它们不能像普通应用程序那样使用普通指针。因此,我必须使用像这样的函数将内存分配到模块的线性内存中:
use std::alloc::{alloc, dealloc, Layout};
#[no_mangle]
pub unsafe fn my_alloc(len: usize) -> *mut u8 {
let align = std::mem::align_of::<usize>();
let layout = Layout::from_size_align_unchecked(size, align);
alloc(layout)
}
这是一个使用 alloc 函数的 JS 函数的例子:
function copyMemory(data, instance) {
var ptr = instance.exports.alloc(data.length);
var mem = new Uint8Array(instance.exports.memory.buffer, ptr, data.length);
mem.set(new Uint8Array(data));
return ptr;
}
我的问题是我不知道如何将此函数转换为 Go,那是因为我卡在了“var mem”行,原因如下:
- 我在 Go 中找不到与“instance.exports.memory.buffer”等价的(实例是“*wasmtime.Instance”类型)。
- 我不知道 Unit8Buffer 在 Go 中是怎么做的。
关于 Wasm 内存的好读物:(https://radu-matei.com/blog/practical-guide-to-wasm-memory/#exchanging-strings-between-modules-and-runtimes)
我花了一些时间来了解 wasmtime Go 包的工作原理,但最终我解决了我的问题:
func main() {
dir, err := ioutil.TempDir("", "out")
check(err)
defer os.RemoveAll(dir)
stdoutPath := filepath.Join(dir, "stdout")
engine := wasmtime.NewEngine()
store := wasmtime.NewStore(engine)
linker := wasmtime.NewLinker(store)
// Configure WASI imports to write stdout into a file.
wasiConfig := wasmtime.NewWasiConfig()
wasiConfig.SetStdoutFile(stdoutPath)
wasi, err := wasmtime.NewWasiInstance(store, wasiConfig, "wasi_snapshot_preview1")
check(err)
// Link WASI
err = linker.DefineWasi(wasi)
check(err)
// Create our module
module, err := wasmtime.NewModuleFromFile(store.Engine, "wasm_file.wasm")
check(err)
instance, err := linker.Instantiate(module)
check(err)
fn := instance.GetExport("greet").Func()
memory := instance.GetExport("memory").Memory()
alloc := instance.GetExport("my_alloc").Func()
// // string for alloc
size2 := int32(len([]byte("Elvis")))
// //Allocate memomory
ptr2, err := alloc.Call(size2)
pointer, _ := ptr2.(int32)
buf := memory.UnsafeData()
for i, v := range []byte("Elvis") {
buf[pointer+int32(i)] = v
}
// Use string func
_, err = fn.Call(pointer, size2 )
check(err)
// Print WASM stdout
out, err := ioutil.ReadFile(stdoutPath)
check(err)
fmt.Print(string(out))
}
我一直在尝试将字符串传递给 Rust 函数(编译为 Wasm),但是据我了解,现在无法直接传递字符串,因为“str”不是“FFI 世界”(至少 Rust 编译器是这么说的):
= help: consider using `*const u8` and a length instead
所以我所做的就是将函数更改为这种形式(而不是使用简单的 &str 类型):
#[no_mangle]
pub extern "C" fn greet(s: *mut u8, len: usize) {
let s = std::str::from_utf8(unsafe { std::slice::from_raw_parts(s, len) }).unwrap();
println!("Hello, {}!", s)
}
这意味着我需要一个指针和u8中字符串的长度。
但是,有人让我注意到 WASM 模块是沙箱化的,因此它们不能像普通应用程序那样使用普通指针。因此,我必须使用像这样的函数将内存分配到模块的线性内存中:
use std::alloc::{alloc, dealloc, Layout};
#[no_mangle]
pub unsafe fn my_alloc(len: usize) -> *mut u8 {
let align = std::mem::align_of::<usize>();
let layout = Layout::from_size_align_unchecked(size, align);
alloc(layout)
}
这是一个使用 alloc 函数的 JS 函数的例子:
function copyMemory(data, instance) {
var ptr = instance.exports.alloc(data.length);
var mem = new Uint8Array(instance.exports.memory.buffer, ptr, data.length);
mem.set(new Uint8Array(data));
return ptr;
}
我的问题是我不知道如何将此函数转换为 Go,那是因为我卡在了“var mem”行,原因如下:
- 我在 Go 中找不到与“instance.exports.memory.buffer”等价的(实例是“*wasmtime.Instance”类型)。
- 我不知道 Unit8Buffer 在 Go 中是怎么做的。
关于 Wasm 内存的好读物:(https://radu-matei.com/blog/practical-guide-to-wasm-memory/#exchanging-strings-between-modules-and-runtimes)
我花了一些时间来了解 wasmtime Go 包的工作原理,但最终我解决了我的问题:
func main() {
dir, err := ioutil.TempDir("", "out")
check(err)
defer os.RemoveAll(dir)
stdoutPath := filepath.Join(dir, "stdout")
engine := wasmtime.NewEngine()
store := wasmtime.NewStore(engine)
linker := wasmtime.NewLinker(store)
// Configure WASI imports to write stdout into a file.
wasiConfig := wasmtime.NewWasiConfig()
wasiConfig.SetStdoutFile(stdoutPath)
wasi, err := wasmtime.NewWasiInstance(store, wasiConfig, "wasi_snapshot_preview1")
check(err)
// Link WASI
err = linker.DefineWasi(wasi)
check(err)
// Create our module
module, err := wasmtime.NewModuleFromFile(store.Engine, "wasm_file.wasm")
check(err)
instance, err := linker.Instantiate(module)
check(err)
fn := instance.GetExport("greet").Func()
memory := instance.GetExport("memory").Memory()
alloc := instance.GetExport("my_alloc").Func()
// // string for alloc
size2 := int32(len([]byte("Elvis")))
// //Allocate memomory
ptr2, err := alloc.Call(size2)
pointer, _ := ptr2.(int32)
buf := memory.UnsafeData()
for i, v := range []byte("Elvis") {
buf[pointer+int32(i)] = v
}
// Use string func
_, err = fn.Call(pointer, size2 )
check(err)
// Print WASM stdout
out, err := ioutil.ReadFile(stdoutPath)
check(err)
fmt.Print(string(out))
}