4

假设我在 Rust 代码中有以下定义:

#[wasm_bindgen]
pub struct RustType {
    foo: usize
}

#[wasm_bindgen]
impl RustType {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self { foo: 100 }
    }
}

#[wasm_bindgen]
pub fn print_addr(obj: &RustType) {
    console_log!("rust addr = {}", obj as *const _ as u32);
}

JS 代码创建一个实例RustType并将其传递给print_addr函数:

var obj = new RustType();
print_addr(obj);

像这样修改生成的print_addr函数后index_bg.js

export function print_addr(obj) {
    _assertClass(obj, RustType);
    console.log("js addr = ", obj.ptr); // <== added this line
    if (obj.ptr === 0) {
        throw new Error('Attempt to use a moved value');
    }
    wasm.print_addr(obj.ptr);
}

在开发控制台中,我得到以下输出:

js addr =  1114120
rust addr = 1114124

问题是为什么 Rust 指针和 JS 指针的值不同?同样根据我的观察,Rust 指针和 JS 指针之间的差异总是等于 4。为什么会这样?

4

1 回答 1

3

如果您查看将 struct 导出到 JS,在页面下方您可以看到为某些函数生成的代码。一个看起来相关的是:

#[export_name = "foo_new"]
pub extern "C" fn __wasm_bindgen_generated_Foo_new(arg0: i32) -> u32
    let ret = Foo::new(arg0);
    Box::into_raw(Box::new(WasmRefCell::new(ret))) as u32
}

所以我们有一个通过 Box 的指针,nbd,但是你可以看到Foo(暴露给 Javascript 的结构)被包裹在 a 中WasmRefCell,这是RefCell 的一个供应版本,但更重要的是一个具有两个字段的结构:

pub struct WasmRefCell<T> {
    borrow: Cell<usize>,
    value: UnsafeCell<T>,
}

T是 Rust 类型,所以在 Rust 类型内部你会看到那个地址,但是给 Javascript 的是 的地址WasmRefCell,这意味着它可能是源中结构之前的地址Cell<usize>: rustc 不保证它将匹配源布局(除非您用 注释结构repr(C)),但在这里它没有什么理由触摸任何东西,所以它没有触摸任何东西也就不足为奇了。

WebAssembly 是一个 32 位架构,因此size_of::<usize>() == 4,返回给 JS 的指针在 Rust 结构中可见的位置之前的 4 个字节。QED。

于 2020-07-27T13:17:19.317 回答