Load a WebAssembly Function Written in Rust and Invoke it from JavaScript

Nik Graf
InstructorNik Graf

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 3 years ago

In this lesson we are going to setup a project from scratch by introducing the JavaScript snippet to load a WebAssembly module. We demonstrate two different ways and showcase the benefit of the streaming solution. Once the module is loaded we can invoke a function previously exported from our Rust code.

Instructor: [00:00] Use Cargo.new to create a new project called utils. Use --lib to declare that it's going to be a library. Next, we need to change the create type to cdylib in Cargo.toml. This has an impact on how things are linked together during compilation.

[00:17] We're now ready to write our first lines of Rust code. Let's get right into it, and create a public function, addOne. The extern keyword is needed to create an interface so that this function can be invoked from other languages.

[00:32] The function accepts a value X, which is an unsigned integer, which is incremented by one and returned. In addition, we need to add a @nomangle annotation to tell the Rust compiler not to mangle the name of this function.

[00:48] Next up, we can use Cargo.build to compile our utils library, targeting WebAssembly. After that, we can run wasm.gc, and output it to utils.gc.wasm in the current directory. Next up, we need to load it somehow. Therefore, we're going to create an index.html file and open a script tag.

[01:10] To load a WebAssembly module, we can fetch it, convert the response to an array buffer, instantiate the result, and then use the WASM module. It has an instance with export, including our addOne function. We can invoke the function and append the result to the DOM.

[01:47] Let's give this a try. We start our web server running HTTP. Once we open a site in the browser, we can see the result. Awesome. Here, you can see that our addOne function printed out the result of adding two plus one.

[02:02] There is one more improvement, though. In the current implementation, we fetch the entire WASM file before we instantiate it. Using instantiate streaming instead, we can stream, compile, and instantiate a WebAssembly module in one go.

[02:17] Especially on slow connections, this can speed up the time to execution a lot. We reload and review the result. Worked like a charm. Be aware, instantiate streaming requires that our WASM file must be served through the content type, application/wasm. Fortunately, our HTTP server already does so.

Nik Graf
Nik Grafinstructor
~ 4 years ago

@Steve totally, just pinged the Egghead team to fix it. thx!

Juliette
Juliette
~ 4 years ago

Hi Nik,

When I run http (from the utils directory) in my terminal, this prompt is returned:

http: error: the following arguments are required: URL

When I run ```http url``, I get an error:

http: error: ConnectionError: HTTPConnectionPool(host='url', port=80): Max retries exceeded with url: / (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x101bff518>: Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known',)) while doing GET request to URL: http://url/

What would be the best way to troubleshoot this issue?

Thanks so much :-)

Mirza Pasic
Mirza Pasic
~ 4 years ago

@Juliette you're not invoking rust http server, your http is most likely an alias for the httpie client. Try this: ~/.cargo/bin/http

Valery Kobzar
Valery Kobzar
~ 4 years ago

Cargo.toml:

... [lib] path = "src/lib.rs" crate-type = ["cdylib"]

Without 'path' variable cargo dont produce utils.wasm file in target/wasw32-unknown-unknown directory

Juliette
Juliette
~ 4 years ago

Thank you @Mirza! I'm going to give that a try tomorrow after my first cup of coffee or so. Cheers:-)

Nik Graf
Nik Grafinstructor
~ 3 years ago

Thanks for helping @Mirza

EdmundsEcho
EdmundsEcho
~ 3 years ago

FYI I needed to prefix my build commands with rustup run nightly cargo build ... in order to avoid crate errors and a suggestion that wasm32-unknown-unknown target may not be installed.

Andrey Kolybelnikov
Andrey Kolybelnikov
~ 3 years ago

WebAssembly.instantiateStreaming is not supported in Safari, and either in Safari iOS, just in case someone like me is going to wonder why the results are different in Chrome and Safari: https://caniuse.com/#search=instantiateStreaming

Jeremy Swanborough
Jeremy Swanborough
~ 2 years ago

I don't have an alias for http, command not found: http also ~/.cargo/bin/http doesn't exist, is there a way to install it?