Skip to main content

Command Palette

Search for a command to run...

[WASM] Set up WASM with Rust

Updated
4 min read

※ I'm neither a native speaker nor an expert. I'm just a noob student who wants to develop the literacy in English and software knowledge. If you find any wrong content or awkward expressions, please feel free to let me know. Thank you!


WebAssembly(wasm) is a binary code that can be executed in a web browser directly. It allows us to use programs written with C, C++, Rust, etc. on the web and achieve high performance close to the native programs. I expect this technology will bring a quite considerable advantage to web applications, so I have just got interested and started to explore WASM recently.

How to start

First, a build tool is required that compiles a native code to a WASM file. For Rust, this tool is named 'wasm-pack'. This can be installed by the following command.

cargo install wasm-pack

https://rustwasm.github.io/wasm-pack/installer/

Then, run the following command to create a new project.

wasm-pack new [project-name]

I named the project name 'first-wasm'. After that, you can see the several files created by wasm-pack. The 'lib.rs' file will be in the 'src' folder. The function named 'greet' is generated by default, and let's add a new function, 'add'.

mod utils;

use wasm_bindgen::prelude::*;

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

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, first-wasm!");
}

#[wasm_bindgen]
pub fn add(a: i32, b: i32) {
    a + b
}

To make a .wasm file, we have to run the command below. There are several options. Since I don't use any bundler at this time, the option '--target web' is provided.

OptionDescription
not specified or bundlerThis assumes a model where the module itself is natively an ES module. To consume the output, however, we need a bundler(e.g. webpack).
nodejsThis is when WebAssembly is deployed into Node.js. We can use the generated JS shims by "require"ing like any other Node module.
webThis option will be used when not using a bundler but still running a code in a web browser.
no-modulesThis is similar to the "web" option. The difference is that it does not support local JS snippets and does not generate an ES module.
wasm-pack build --target web

// Using this, wasm-pack execute the following commands under the hood.

// rustup target add wasm32-unknown-unknown
// cargo build --target wasm32-unknown-unknown --release

After that, the "pkg" folder is generated, containing the .wasm file, the .ts file for a function declaration, and the .js file as a glue code. If you take a look at the .js file, you can see the module functions that are in src.rs (greet, add) and "__wbg_init". The latter is what we have to call to load a WASM module.

export function greet() {
    wasm.greet();
}

/**
* @param {number} a
* @param {number} b
* @returns {number}
*/
export function add(a, b) {
    const ret = wasm.add(a, b);
    return ret;
}

// ...

async function __wbg_init(input) {
    // ...
}

export default __wbg_init;

Now, let's use these functions on the web. To see how it works, I made a brief HTML file and a node server. (I have no idea how to represent directories properly.)

node
|--js
|  |--pkg
|  |--index.js
|--routes
|--|--index.js
|--app.js
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>
            hello wasm
        </title>
    </head>
    <body>
        <div>
            Hello
        </div>        
        <script type="module" src="/scripts/index.js"></script>
    </body>
</html>
// app.js

const express = require('express');
const path = require('path');

const app = express();
const pageRouter = require('./routes/index.js');

app.set('port', process.env.PORT || 3000);

app.use(express.static(path.join(__dirname, './views')));
app.use('/scripts', express.static(path.join(__dirname, 'js')));
app.use(express.json());
app.use(express.urlencoded({extended: false}));

app.use('/', pageRouter);

app.use('/', (err, req, res, next) =>
{
    res.locals.message = err.message;
    res.status(err.status || 500);
    res.render('error');
});

app.listen(app.get('port'), () => {
    console.log("Server listening on port ", app.get('port'));
});

This index.js file is the code actually to use the WASM module. The 'init' function must be followed by any other functions. Otherwise, the WASM might not be loaded successfully, leading to an error.

// js/index.js
import init, {greet, add} from './pkg/first_wasm.js';

// Since the init function is defined as async function, 
// we have to use async/await pattern.
const run = async () =>
{
    await init();

    greet();
    console.log(add(1, 2));
}

run();

/* you can write using Promise.then method also.
const run = () =>
{
    greet();
    console.log(add(1, 2));
}

init().then(run);

Once you run the server and access localhost:3000, The alert message will be activated and the result of the addition will be logged in the console.

That's all! I should take a deeper dive into wasm-bindgen and Rust(I didn't understand how the attribute #[wasm-bindgen] works). Also, I will add the content using bundler(webpack) here.


References

The guide about wasm-bindgen

The guide about wasm-pack


written by Sang Hyeok Park

More from this blog

[ZSH] tree 사용하기

들어가며 큰 규모의 프로젝트를 출시한 뒤, 후일을 위해서 더 늦기 전에 파일 정리 및 문서화를 진행해야했다. 문서화 작업을 하는 중에 기왕 정리하는 거 파일 구조를 이쁘게 트리 구조로 나열하여 코멘트를 달면 나중에 보더라도 이해하기 더 쉬울 것 같았다. 어떻게 해야 간지나는 트리 구조를 만들 수 있을까 방법을 찾다보니 역시나 파일 구조를 트리로 이쁘게 출력해주는 커맨드 툴이 존재했다. tree 커맨드에 대해서 알아보고 알짜배기 내용만 정리했다....

Feb 21, 20242 min read

[Next.js] parallel routes & intercepting routes

트위터 로그인 모달창을 만들어보며 넥스트의 parallel routes 와 intercepting routes 을 학습한 내용을 정리해보았습니다. 트위터 로그인 창을 확인해봅시다. 루트 디렉토리 화면을 배경으로 i/flow/login 페이지가 동시에 표시되고 있습니다. 저는 app router 를 학습하기 전까지는 createPortal 을 사용하여 포탈 영역에 로그인 컴포넌트를 띄우는 방식을 사용했었습니다. const NoLogin =()=...

Feb 1, 20244 min read

C/C++ 이진 트리(binary tree) 개요 및 구현(1)

개요 트리는 노드들이 나무 가지처럼 연결된 비선형 계층적 자료구조이다. 하위 트리가 존재하고, 그 노드에 또 하위 트리가 존재하는 자료구조 이다. 트리의 맨 위에 있는 루트 노드가 존재한다. 우리가 알아볼 트리는 이진 트리이다. 이진 트리는 자식 노드(부모로부터 아래로 이어진 노드)가 2개 이하인 구조를 말한다. 트리의 사용 사례로는 다음과 같다 계층 적 데이터 저장(파일,폴더) 효율적인 검색 속도 힙 데이터 베이스의 인덱싱 트리에 ...

Jan 31, 20244 min read

[React] Server component (RSC)

React.js 18 에 도입된 리액트 서버 컴포넌트는 서버에서 동작하는 리액트 컴포넌트를 의미합니다. Next가 권장하는 라우팅 방식인 app router의 기반이 되는 컴포넌트이기 때문에 app router 를 이해하기 위해서는 server component 에 대한 이해가 필요합니다. server component 리액트는 클라이언트단만을 컴포넌트화하는 대신, server component라는 개념을 통해 서버 영역을 컴포넌트화합니다. ...

Jan 29, 20243 min read

Flutter, JavaScript

42 posts