Semantics of args / return types in wasm32-unknown-unknown target

I'm currently playing around with wasm in combination with Python. When I looked at wasm bindgen it seemed very much geared towards JS interop. Therefore I started to look into using the wasm target without wrapper. However, I have trouble understanding the semantics of argument and return types.

I assume the primitives types that map directly to wasm (i32, i64, f32, f64) are mapped as is. Similary, I assume pointers map to i32. When I started to play around with more complex types it seems that the following types are also mapped to pointers (i32):

  • returning (and accepting) Boxes (whereas Rc's seem to not work)
  • mut and non-mut references

Since, I'm a bit afraid to simply go on trial and error to define my interface, I was wondering whether the semantics how Rusts maps arg and return types to wasm documented somewhere? Google and the forum search turned up empty.

Box doesn't have a guaranteed abi. That it is passed as pointer may change at any moment. References however are guaranteed to be passed the same way as pointer. Structs are passed differently depending on the wasm target used. The wasm32-wasi and wasm32-unknown-emscripten targets follow the official wasm C abi for #[repr(C)] structs. The wasm32-unknown-unknown target uses a weird abi by accident, but wasm-bindgen depends on, so it can't be changed for the time being. Basically they are recursively split into individual primitives which are then each passed as separate argument. Please prefer only passing and returning simply primitives and pointers and avoid passing structs or enums. Note that when #[repr(C)] is not used, structs don't have a guaranteed abi or memory layout.

1 Like

Thanks a lot. That clears things up quite a bit :slight_smile:

Surprisingly, it actually does. From std::boxed:

So long as T: Sized , a Box<T> is guaranteed to be represented as a single pointer and is also ABI-compatible with C pointers (i.e. the C type T* ). This means that if you have extern "C" Rust functions that will be called from C, you can define those Rust functions using Box<T> types, and use T* as corresponding type on the C side.

4 Likes

Very cool. Thanks a lot for the clarification. I can now simplify a lot of FFI code even without WASM in the mix :slight_smile: