WASM32 Target and C Libraries

Hi,

A ton of crates wrap native libraries written in C. I'd like to use their functionality, but I'm targeting wasm32-unknown-unknown for web browsers. Is there a way to get this working, for example by compiling the library with emscripten and then somehow linking them together?

I know that it should be possible to write a wrapper in C that exposes a JS API, compile it to wasm using emscripten and then talk to that over the browser APIs, but that adds a ton over overhead, is a lot of work and also that way I can't use the existing crates. Is there a better solution?

1 Like

If you want to use a non-Rust native library from WebAssembly you'll need to compile the native library to WebAssembly (e.g. using emscripten, as you mentioned). This can either be really easy or really hard depending on what the library does - something which is pure algorithms and behaviour like a compression library is easy, but something that interacts with the OS or hardware (e.g. graphics or the file system) could be a pain.

From there, your Rust code should be able to declare the C functions using a normal extern block. You'll need to put the #[wasm_import_module] attribute above the block so rustc knows which WebAssembly module to import the C functions from.

Then when loading the Rust WebAssembly (e.g. WebAssembly.instantiateStreaming()) you'll need to pass in the compiled C WebAssembly as part of the importObject argument, making sure the property it is assigned to matches the wasm_import_module from earlier.

I doubt wasm-bindgen will do any of these steps for you, so you've kinda got to implement it yourself. You may want to create an issue on their issue tracker asking if it's on the roadmap. It also looks like there was some experimentation with using wasm-bindgen from other languages, but I don't quite think that's what you want.

Well if you've got to compile the C code to WebAssembly using emcsripten anyway (because rustc isn't a C compiler) and emcsripten generates bulky binaries or slow code, won't wrapping it in Rust just add more overhead?

Or are you talking about passing Rust JavaScript functions which proxy to the C WebAssembly? Normally when I write code that interfaces with another language I'll try to keep the bindings as high-level and self-contained as possible. So your Rust might load all your inputs into a struct and send that to the C code which then implements the entire operation and gives you the final result.

Having code that constantly calls into another language for small operations (e.g. to do a field lookup) tends to feel clunky and may have a performance impact if done (e.g. you may need to marshal data, and it's also going to be a virtual function call). This is what I see when doing FFI in native code, so I'm guessing it'll be similar for WebAssembly.

2 Likes

I'm not aware whether emscripten generates bad wasm code. I'm only talking about using a C library within a Rust web application, so Rust doesn't add overhead because it's already there anyways.

The big advantage of Rust (over lua, C# and Java for example) when it comes to integrating with C is that you don't have to write any interface code in C, everything can be Rust.

Yes, that's why there are Rust crates wrapping the C libraries. They take care of making the calls safe. That's why I'd like to keep using the existing ones, rather than having to write new ones for wasm32.

Native Rust code can use C data without any marshalling. It's possible to convert for example const char* to std::string::String, but it's optional.