ทดลอง Compile Rust เป็น WebAssembly

Published on
WASM
hello-wasm
Discord

สืบเนื่องจากช่วงนี้ผมสนใจ WebAssembly เป็นพิเศษ เนื่องจากสนใจ Rust ด้วย และคิดว่ามันก็น่าสนใจ และมักจะเห็นคนพูดถึงสองตัวนี้บ่อยๆ ก็เลยพยายามศึกษาเรื่อยๆ เวลาว่างๆ เอาจริงๆ ก็ยังไม่ค่อยรู้อะไรเท่าไหร่ เน้นอ่าน ลองเล่น เข้าใจบ้าง ไม่เข้าใจบ้าง วันนี้ก็เลยมาเขียนบล็อกบันทึกเอาไว้ซะหน่อย

WebAssembly คืออะไร?

WebAssembly หรือ WASM คือ low level bytecode ที่รันบนเว็บ โดยเขียนได้หลายภาษา (ที่รองรับการ compile เป็น WASM) เช่น C/C++ หรือ Rust ตัว concept WASM คือเป็น binary format ที่สามารถทำงานร่วมกับ JavaScript ได้ และปัจจุบัน WASM ก็รองรับ Browser หลักๆ เกือบหมดแล้ว

เริ่มต้น WebAssembly

เริ่มจาก Getting Started จากเว็บ Official เลย จากนั้น ก็เลือกภาษา Rust ตัว Tutorial ก็จะเป็นของ Mozilla Developer

เราสามารถใช้ Rust และ WebAseembly ร่วมกันได้ แบบ 2 use cases ใหญ่ๆ คือ

  1. เขียนทั้งแอพด้วย Rust แล้ว compile เป็น WASM
  2. เขียนด้วย Rust แค่บางส่วน แล้วใช้ร่วมกับ JavaScript frontend ก็ได้

ตัว Rust/Wasm Framework ชื่อ yew เป็นอีกตัวที่ผมสนใจ เพราะดูคล้ายๆ React + Elm อยู่ใน list ที่ว่าจะลองศึกษาดู

Create Project

  1. ติดตั้ง Rust

เริ่มต้น Install Rust ด้วย rustup - หากไม่เคยติดตั้ง แนะนำ อ่านเพิ่มเติม มาหัดเขียนโปรแกรมด้วยภาษา Rust กันเถอะ

  1. ติดตั้ง wasm-pack - เป็นตัวช่วยที่ให้เรา build ไฟล์ Rust เป็น WASM รวมถึง build package ที่สามารถไปรันบน Browser ได้เลย
cargo install wasm-pack
  1. สร้าง folder ใหม่ ชื่อ hello-wasm
cargo new --lib hello-wasm

จะมีไฟล์ในโฟลเดอร์ hello-wasm แบบนี้

├── Cargo.toml
└── src
    └── lib.rs

1 directory, 2 files

เพิ่ม wasm-bindgen ใน Cargo.toml และ [lib]

Cargo.toml
[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = "0.2.80"

แนะนำ Rust Analyzer เป็น VS Code Extension สำหรับเขียนภาษา Rust

  1. Update lib.rs เป็นโค๊ดที่ใช้ wasm_bindgen (ใช้สำหรับเขียน Rust กับ JavaScript เช่น Rust เรียก function JavaScript หรือ JS เรียก Rust ได้)
lib.rs
use wasm_bindgen::prelude::*;

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

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

จากโค๊ดนี้เราสามารถเรียก alert() ที่เป็น JavaScript function จาก Rust ได้

  1. Compile โปรแกรมดู เพื่อ compile จาก Rust เป็น WebAssembly
wasm-pack build --target web

จะได้ผลลัพธ์ประมาณนี้

[WARN]: ⚠️   origin crate has no README
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: found wasm-opt at "/opt/homebrew/bin/wasm-opt"
[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 5.49s
[INFO]: 📦   Your wasm pkg is ready to publish at /dev/hello-wasm/pkg.

เราจะได้โฟลเดอร์ pkg ข้างในมี WASM และไฟล์ JavaScript ที่เราสามารถเอาไปใช้บนเว็บได้เลย

WASM on Web

ตัวอย่าง ลองสร้างไฟล์ index.html ง่ายๆ ขึ้นมา

index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hello WASM</title>
</head>
<body>
  <script type="module">
    import init, { greet } from './pkg/hello_wasm.js';

    init().then(() => {
      greet("WebAssembly");
    });
  </script>
</body>
</html>

ลอง Start server จากไฟล์ index.html

npx serve

จะได้เว็บ http://localhost:3000

หรือใครใช้ Python ก็รัน

python3 -m http.server

จะได้เว็บ http://localhost:8000

เมื่อลองเปิดเว็บ จะเห็น Alert popup ว่า Hello, WebAssembly!

สิ่งที่สังเกต จะเห็นว่า JavaScript เรียก function ชื่อ greet() ซึ่ง function นี้คือ function ใน lib.rs ที่เขียนด้วยภาษา Rust เพื่อ alert JavaScript ผ่าน wasm-bindgen นั่นเอง

สรุป

WASM เป็นอะไรที่น่าสนใจมากสำหรับผม และก็เป็นอะไรที่ค่อนข้างยากเช่นกัน สิ่งที่ผมเรียนรู้ได้ ก็คือพยายามฝึก หัดลอง ทำนู้นทำนี่ แล้วเดี๋ยวมันก็จะเริ่มเข้าใจอะไรมากขึ้นเอง พอจับต้นชนปลายได้ นิดๆหน่อยๆ ก็เป็นอะไรที่น่าสนุกดี แม้ว่าตอนนี้บอกตรงๆ ว่ายังไม่รู้ว่าจะเอามาทำอะไร 🤣

References

Buy Me A Coffee
Authors
Discord