WebAssembly (以下簡稱Wasm) 是一種高效能的二進位格式,可以在現代瀏覽器中運行近乎原生速度的程式碼。這讓 Wasm 成為在網頁上執行高效能應用的理想技術。Rust 是支援 Wasm 編譯的語言之一,其強大的編譯器能夠將 Rust 程式碼轉換為 Wasm,這使得 Rust 開發者能夠輕鬆將高效能應用程式部署到網頁上。
在這篇文章中,我們將介紹 Wasm 與 Rust 的整合,並通過建立一個簡單的 Wasm 應用來演示如何將 Rust 程式碼嵌入到網頁中運行。
Wasm 是一種經過精心設計的低階字節碼格式,旨在讓程式能夠在瀏覽器內以接近原生應用的速度運行。它不僅僅是一種效能提升工具,更是開啟了全新網頁應用時代的技術基礎。Wasn 的出現改變了我們對於瀏覽器內部效能的認知,尤其對於那些曾經需要依賴桌面應用來達到高效能需求的領域,如 3D 遊戲、圖像處理、科學計算等。
Wasm 的關鍵特點包括:
跨平台無縫運行:Wasm 無需依賴特定平台,可以在所有現代瀏覽器上運行,從而消除了過去需要為不同設備編寫不同版本程式碼的麻煩。這種跨平台的能力,不僅提升了開發效率,還為用戶提供了統一、流暢的使用體驗。
接近原生速度的效能:Wasm 在瀏覽器中以接近原生的速度運行,這對於如高效能遊戲、視訊編碼、數據可視化等應用至關重要。以前這些應用往往只能在桌面或專用硬體上實現,但有了 Wasm,這些高效能需求的應用如今也能在瀏覽器中輕鬆執行,並且無需為不同的硬體平台特別優化。
語言無關性:Wasm 的架構允許多種語言編譯為 Wasm 格式,如 C、C++、Rust 等。這意味著開發者可以使用最適合解決問題的語言,而不必拘泥於某個語言的限制。Rust 作為一個現代化系統級語言,因其記憶體存安全、高效能特性,與 Wasm 的結合變得特別強大,使得 Rust 成為開發高效能網頁應用的理想選擇。
透過 Wasm,網頁應用的潛力不再局限於腳本語言的範疇,它為我們打開了運行複雜應用、實現真實效能需求的大門。而 Rust,作為 Wasm 生態中支援度最強的語言之一,則進一步放大了這一潛力,成為推動網頁開發革新的關鍵力量。
首先,確保你已經安裝了 Wasm 的相關工具。Rust 提供了 wasm-pack
來幫助開發者將 Rust 編譯成 Wasm,並整合到 JavaScript 環境中。
可以通過以下命令來安裝 wasm-pack
,安裝可能需要等一段時間:
cargo install wasm-pack
接下來,使用 cargo
建立一個新的 Rust 專案:
cargo new --lib hello_wasm
cd hello_wasm
這會建立一個 Rust 函式庫專案,我們將在其中編寫 Wasm 相關的程式碼。
在專案的 Cargo.toml
檔案中,加入 Wasm 相關的設定:
[package]
name = "hello_wasm"
version = "0.1.0"
edition = "2021"
[dependencies]
wasm-bindgen = "0.2"
[lib]
crate-type = ["cdylib"]
wasm-bindgen
是一個可以讓 Rust 程式與 JavaScript 互操作的工具,我們將使用它來將 Rust 函數導出給 JavaScript 調用。[lib] crate-type = ["cdylib"]
這個設定,簡單來說,我們是在告訴 Rust:「嘿,這個專案最後要輸出一個可以被其他程式(例如 JavaScript)使用的東西,所以請把它編譯成一個特別格式,讓它可以和其他程式合作。」
cdylib
這種類型的庫使用 C 語言的 ABI(應用二進制介面),而 C ABI
是大多數語言都支持的標準。因此,當我們想要讓 Rust 與 JavaScript(透過 Wasm)或其他語言互動時,cdylib
能讓 Rust 程式輸出一個其他語言能理解的格式。
這個設定幫助我們把 Rust 程式編譯成一種格式,讓瀏覽器中的 JavaScript 透過 Wasm 能夠讀取和執行 Rust 的功能。
在 src/lib.rs
中撰寫以下簡單的 Rust 函數:
use wasm_bindgen::prelude::*;
// 將 Rust 函數導出給 JavaScript 調用
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
use wasm_bindgen::prelude::*;
這段程式碼是在引入 wasm-bindgen 庫中的預設功能(即 prelude),讓你可以使用 wasm-bindgen 提供的各種功能來與 JavaScript 互動。
這段程式碼定義了一個 greet
函數,接受一個字串參數,並返回一個問候訊息。通過 #[wasm_bindgen]
巨集,讓這個函數可以被 JavaScript 調用。
使用 wasm-pack
將 Rust 編譯為 Wasm:
wasm-pack build --target web
這會生成一個 pkg
資料夾,其中包含了 .wasm
檔案以及相應的 JavaScript 包裝程式碼,可以直接在網頁中使用。
在專案目錄下建立一個簡單的 index.html
文件,並載入剛才編譯好的 Wasm 檔案:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rust WebAssembly</title>
</head>
<body>
<h1>Rust 與 WebAssembly 範例</h1>
<input type="text" id="name" placeholder="輸入你的名字" />
<button id="greet">打招呼</button>
<p id="output"></p>
<script type="module">
import init, { greet } from './pkg/hello_wasm.js';
async function run() {
await init();
const button = document.getElementById("greet");
const input = document.getElementById("name");
const output = document.getElementById("output");
button.addEventListener("click", () => {
const name = input.value;
output.textContent = greet(name);
});
}
run();
</script>
</body>
</html>
你的 index.html
應該建立在專案的根目錄下,與 Cargo.toml
位於同一層級。這樣可以確保 Wasm 編譯後的檔案能夠正確被載入並與網頁整合。
通常來說,專案的目錄結構會像這樣:
my_project/
│
├── src/
│ └── lib.rs # Rust 程式碼
│
├── Cargo.toml # 專案的 Cargo 設定檔
├── index.html # 你的 HTML 檔案
└── pkg/ # WebAssembly 編譯後生成的 JavaScript 與 Wasm 檔案
└── hello_wasm.js # 編譯後的 WebAssembly 模組
當你執行 wasm-pack build
來編譯 Rust 到 Wasm 後,生成的 Wasm 檔案(如 .wasm
和對應的 JavaScript 模組 hello_wasm.js
)會存放在 pkg/
資料夾內。這時你的 index.html
就可以從 pkg/
資料夾中載入這些檔案來實現互操作。
這個 HTML 檔案提供了一個簡單的使用者介面,允許使用者輸入名字,然後點擊按鈕顯示問候訊息。JavaScript 會載入 Wasm 模組,並調用 Rust 定義的 greet
函數。
miniserve
是一個簡單且強大的靜態文件伺服器工具,可以用來運行本地的 Wasm 項目。以下是如何使用 miniserve
運行你的專案:
miniserve
首先,需要安裝 miniserve
,你可以通過以下命令來安裝它:
cargo install miniserve
安裝完成後,在專案根目錄執行以下命令來啟動伺服器:
miniserve . --index index.html
這個命令將把當前目錄設為伺服器的根目錄,並設置 index.html
作為默認首頁。miniserve
會在 http://localhost:8080
提供網頁。
打開瀏覽器,訪問 http://localhost:8000
,你將看到網頁正常運行,並可以輸入名字並使用 Rust 生成問候訊息。
我們可以進一步擴展這個應用,來展示 Rust 和 Wasm 的性能。我們將實作一個計算階乘的函數,並在網頁中運行。
lib.rs
當中新增階層運算函數use wasm_bindgen::prelude::*;
// 將 Rust 函數導出給 JavaScript 調用
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
#[wasm_bindgen]
pub fn factorial(n: u32) -> u32 {
(1..=n).product()
}
這段程式碼定義了一個計算階乘的 Rust 函數。
index.html
在 HTML 中新增一個輸入框和按鈕,讓使用者輸入數字並計算其階乘。
<input type="number" id="number" placeholder="輸入一個數字" />
<button id="calculate">計算階乘</button>
<p id="result"></p>
<script type="module">
import init, { factorial } from './pkg/hello_wasm.js';
async function run() {
await init();
const calculateButton = document.getElementById("calculate");
const numberInput = document.getElementById("number");
const resultOutput = document.getElementById("result");
calculateButton.addEventListener("click", () => {
const num = parseInt(numberInput.value, 10);
resultOutput.textContent = `階乘結果:${factorial(num)}`;
});
}
run();
</script>
記得再次執行 wasm-pack build --target web
指令,讓函數被打包到 pkg
當中,接著我們可以透過 miniserve . --index index.html
將 index.html
開啟到 http://localhost:8080
當中,並且由瀏覽器互動。
在這個範例中,當使用者輸入一個數字並點擊按鈕後,Rust 的 factorial
函數會被調用並返回計算結果,如下圖所示:
當然,階層運算結果如果發生 溢位
了,輸出結果就會為0,因為我們所設定的資料類別為 u32
,所以如果將資料類別改為 u128
則就能儲存更大的數字位數。
我們已經展示了如何將 Rust 程式編譯為 Wasm,並在網頁中調用它的基本流程。接下來,我們將更深入探討 Rust 與 Wasm 的整合如何在實際應用中發揮其強大功能。
Rust 在記憶體管理上有著無可比擬的優勢,它以「所有權」系統來避免許多常見的記憶體錯誤,例如空指標引用或記憶體洩漏。而這種特性在 Wasm 中同樣適用,能夠保證在網頁應用中執行的程式碼擁有極高的記憶體安全性,減少崩潰的風險。這對於那些需要高可靠性的網頁應用(如金融應用或醫療數據處理)至關重要。
Wasm 的一個核心優勢是能夠處理計算密集型任務。Rust 作為系統級語言,擅長進行精細控制,並且能夠直接管理硬體資源。結合 Wasm,可以讓那些需要高效運算的任務(例如 3D 渲染、加密運算、科學計算)以接近原生的速度在瀏覽器中執行。這意味著,我們可以在網頁上實現以前只能在桌面應用中達到的效能。
儘管 Wasm 可以處理複雜的計算,JavaScript 仍然是瀏覽器中的主導語言。通過 wasm-bindgen
,Rust 能夠與 JavaScript 進行高效互操作,這允許開發者根據具體需求,將高效能的部分用 Rust 編寫成 Wasm,而將其他邏輯用 JavaScript 實現。這種分工可以提升整體應用的可維護性和擴展性。
例如,在一個需要即時影像處理的網頁應用中,影像處理的核心邏輯可以使用 Rust 寫成 Wasm 模組,負責繁重的運算。JavaScript 則負責處理用戶界面、交互以及 API 調用等較輕量的任務。這樣的組合可以大大提升效能,並保持良好的開發體驗。
Wasm 不僅能在瀏覽器中運行,還可以在其他環境如伺服器或 IoT 設備中使用。Rust 的跨平台特性與 Wasm 的無縫整合,意味著開發者可以使用同一套程式碼,來部署在多個不同平台上運行,減少了重寫程式碼的需求並提升了開發效率。
除了在瀏覽器中使用 Wasm,Rust 還有潛力成為 伺服器端 Wasm 的一部分。未來,伺服器端應用可以利用 Wasm 將一些邏輯部署在伺服器上運行,並透過 Wasm 確保跨平台和高效能運行,這種模式有望改變雲端計算的現有格局。
在這篇文章中,我們介紹了如何將 Rust 程式碼編譯為 Wasm,並將其嵌入到網頁中運行。我們通過簡單的問候範例,學習了 Rust 與 Wasm 的整合,並且進一步展示了計算階乘的應用場景。
Rust 與 Wasm 的結合,讓開發者能夠在網頁中運行高效能的程式,同時保持記憶體安全和程式的穩定性。這對於構建計算密集型的應用程式(如遊戲、視訊處理)具有極大優勢。
下一篇,我們將進一步展示 Rust 使用 Wasm 在網頁上運作時,如何能夠更進一步結合 React 完成高效的網頁開發。