iT邦幫忙

2023 iThome 鐵人賽

DAY 28
1
Software Development

為你自己學 Ru.....st系列 第 28

[為你自己學 Rust] Rust 與 WebAssembly

  • 分享至 

  • xImage
  •  

如果說以一個前端工程師來說,學習 Rust 除了練到一些系統程式之外,另一個好處就是 Rust 可以編譯成 WebAssembly(wasm)。

什麼是 WASM

跟 JavaScript 比起來,wasm 是相對更低階、效能更好的可執行程式,目前主流的瀏覽器都有支援,甚至有不少厲害的產品都已經用它開發,像是設計師們很喜歡用的 Figma,還有近期 Adobe 的 PhotoShop 線上版,都是 wasm 做出來的。

雖然 wasm 本身其實不太好寫,目前已有不少程式語言可以直接進行轉換,例如 C、C++、Go 等程式語言都行,Rust 也是其中一個,也就是說,我們可以其它程式語言寫,最後編譯成 wasm 檔案。

wasm 目前通常是用在比較效能吃緊的地方,雖然我們現在還沒有什麼需要拼效能的地方,但還是就來做一個計算 Fibonacci 的功能試試看吧

前置動作

要把專案編譯成 wasm 有些前置動作,根據官網手冊的說明,會先需要安裝 wasm-pack 程式,我們就直接用 cargo 指令來安裝它:

$ cargo install wasm-pack

要注意的是這裡不是 add 而是 installadd 是把套件加進 Cargo.toml 裡,然後在這個專案裡就能使用它;而 install 是把程式安裝到系統裡,在任何地方都能獨立執行。

建立新的函式庫專案

這個我們之前有學過,用 --lib 就能搞定:

$ cargo new fib --lib
Created library `fib` package

接著打開 src/lib.rs 寫上可以計算 Fibonacci 的函數:

pub fn fib(n: i32) -> u64 {
    if n <= 0 {
        panic!("不能小於或等於零");
    }

    match n {
        1 => 1,
        2 => 1,
        3 => 2,
        _ => fib(n - 1) + fib(n - 2),
    }
}

不算太難,用 Recursive 搭配 match 做 Pattern Matching 寫起來還滿簡單的。

接著,在這個專案裡還需要裝個套件:

$ cargo add wasm-bindgen

這是用來讓 Rust 與 JavaScript 兩邊可以互相溝通的東西。最後,在 Cargo.toml 裡的 [lib] 段落裡還要再加一段 crate-type = ["cdylib"] 的設定,如果 [lib] 不存在就自己手動加上去,最後檔案的內容看起來會像這樣:

[package]
name = "fib"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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

[dependencies]
wasm-bindgen = "0.2.87"

wasm-bindgen 的版號可能會隨著時間而有所不同。

設定屬性

再回到剛剛寫好的函數前面加上專用的屬性:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn fib(n: i32) -> u64 {
    // ... 略 ...
}

這樣就可以準備來進行編譯了。

編譯成 wasm

要把 Rust 專案編譯成 wasm 的話可以使用剛才安裝的 wasm-pack 程式並指定編譯的 target:

$ wasm-pack build --target web
[INFO]: 🎯  Checking for the Wasm target...
[INFO]: 🌀  Compiling to Wasm...
   Compiling proc-macro2 v1.0.69
   Compiling unicode-ident v1.0.12
   Compiling wasm-bindgen-shared v0.2.87
   ... 略 ...
   Compiling fib v0.1.0 (/private/tmp/fib)
    Finished release [optimized] target(s) in 3.73s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨   Done in 3.92s
[INFO]: 📦   Your wasm pkg is ready to publish at /private/tmp/fib/pkg.

這樣就編譯完成了!

給 JavaScript 使用

編譯完成的檔案會在 pkg 目錄裡,所以我另外建一個全新的 HTML 專案,裡面擺一個 index.htmlapp.js,不想分開寫也可以省略 app.js,直接在 HTML 裡寫 <script> 也行。最後把剛剛編譯好的 pkg 目錄搬一份過來,現在目錄大概會像這樣:

├── app.js
├── index.html
└── pkg
    ├── fib.d.ts
    ├── fib.js
    ├── fib_bg.wasm
    ├── fib_bg.wasm.d.ts
    └── package.json

接著編輯 index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script type="module" src="app.js"></script>
  </body>
</html>

app.js 的內容如下:

import init, { fib } from "./pkg/fib.js";
init().then(() => {
  console.log(fib(20));
});

這裡從剛剛編譯好的檔案裡的其中一個 .js 檔 import 東西進來,fib 就是剛剛在 Rust 裡寫的那個函數。最後一步,打開瀏覽器檢視 index.html,應該就會看到 Console 印出東西來了:

為你自己學 Rust

這裡會看到尾巴有個 n 是因為這是一個 JavaScript 的 BigInt 的型態,如果想要轉成數字再加個 Number() 就行了。

就這樣,我們在 Rust 裡寫好的程式,就能編譯成 wasm,然後給 JavaScript 呼叫了 :)


上一篇
[為你自己學 Rust] 寫函式庫給自己跟別人用
下一篇
[為你自己學 Rust] 建立桌面應用程式 - Tauri
系列文
為你自己學 Ru.....st30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言