How WebAssembly is implemented in Rust: briefly

How WebAssembly is implemented in Rust: briefly

Hello, Habre!

Rust – a language that in recent years has won the hearts of developers with its reliability, and especially security. WebAssembly in turn, it allows us to bring the performance and power of native applications to the browser. Together, these tools allow you to create web applications with high speed, and ultimately – high security.

p.s.: it is assumed that the reader knows the basics of webassembly and something about rust

Realization

wasm-bindgen

wasm-bindgen is a library and command-line tool that facilitates interaction between Rust and JavaScript code, allows you to call JavaScript functions from Rust and vice versa, and work with different data types.

You can call JavaScript functions directly from Rust. This allows you to use browser and web API capabilities without leaving the Rust ecosystem.

wasm-bindgen allows you to export Rust functions to WebAssembly that can then be called from JavaScript, and it also facilitates the exchange of complex data types between Rust and JavaScript, such as strings, structs, and even entire classes.

Let’s say we have the following JavaScript function that outputs a message to the console:

function greet(name) {
    console.log(`Hello, ${name}!`);
}

We call this function from Rust as follows:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn greet(name: &str);
}

#[wasm_bindgen(start)]
pub fn run() {
    greet("World");
}

Now let’s do the opposite. Let’s write a function in Rust and call it from JavaScript:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(x: i32, y: i32) -> i32 {
    x + y
}

Now we can call this function from JavaScript:

import { add } from './my_module';

console.log(add(5, 7)); // Выведет 12

wasm-bindgen also allows working with Rust JavaScript frameworks. Example:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct Point {
    x: i32,
    y: i32,
}

#[wasm_bindgen]
impl Point {
    pub fn new(x: i32, y: i32) -> Point {
        Point { x, y }
    }

    pub fn x(&self) -> i32 {
        self.x
    }

    pub fn y(&self) -> i32 {
        self.y
    }
}

In JavaScript, we can create and use an object Point Yes:

import { Point } from './my_module';

const p = Point.new(2, 3);
console.log(p.x()); // Выведет 2
console.log(p.y()); // Выведет 3

wasm-pack

wasm-pack is a tool for compiling Rust code in WebAssembly and managing the entire process from start to finish.

wasm-pack compiles Rust code into WebAssembly, optimizing it for use on the web. After harvesting wasm-pack helps package the project into an npm-compatible format, making it easier to distribute and use.

wasm-pack supports code testing on WebAssembly.

Installation wasm-pack can be done through cargo:

cargo install wasm-pack

Then we create a new project using cargo:

cargo new --lib my_wasm_project
cd my_wasm_project

In the file Cargo.toml project dependency is indicatedwasm-bindgensince wasm-pack works in tandem with him:

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

In the file src/lib.rs let’s create a simple function:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)

Now you can assemble the project using wasm-pack:

wasm-pack build

The command will create a folder pkgwhich will contain the compiled WebAssembly code and automatically generated JavaScript bindings.

After a successful build, you can publish your package to npm:

wasm-pack publish

The WebAssembly module is now ready for use in a JavaScript project. You can import and use the function greet in JavaScript code:

import { greet } from 'my_wasm_project';

console.log(greet('World')); // Выведет "Hello, World!"

cargo-web

cargo-web provides an easy-to-use local web server that automatically reloads your application when the code changes.

First, let’s install cargo-web:

cargo install cargo-web

Then we will create a new project:

cargo new --lib my_web_project
cd my_web_project

IN Cargo.tomlspecify the assembly type and add dependencies:

[package]
name = "my_project"
version = "1"
edition = "2"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

IN src/lib.rs let’s add a simple function that will be used in the web application:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn say_hello() -> String {
    "Hello from Rust!".to_string()
}

Now you can start your local web server with cargo-web:

cargo web start

This command will start the web server and open the application in the browser. Any changes to the code will be reflected automatically.

To use the function say_hello in the web page, let’s create an HTML file:

<!DOCTYPE html>
<html>
<head>
    <title>Rust WebAssembly App</title>
    <script type="module">
        import init, { say_hello } from './my_web_project.js';

        async function run() {
            await init();
            alert(say_hello());
        }

        run();
    </script>
</head>
<body>
    <h1>Hello from Rust and WebAssembly!</h1>
</body>
</html>

This code will load and initialize the generated WebAssembly module and then call the function say_hello.


Rust has good tools for implementing WASM. Thank you for reading the article

Continuing the topic, I want to remind you about free webinars from market experts about safe and unsafe Rust and how Rust encourages the use of composition.

And more courses from OTUS experts can be found in the full catalog.

Related posts