为什么 Rust WASM 指针和 JS 指针的值不同?

Why do the values of Rust WASM pointer and JS pointer differ?

假设我在 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);

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

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 指针之间的 diff 总是等于 4。为什么会这样?

如果您查看 exporting a struct to 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 的结构)被包裹在 WasmRefCell 中,这是 a vendored version of RefCell 但更重要的是具有 两个 字段的结构:

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

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

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