事件背景:小型函式庫,卻有龐大的相容性表面
這起案例來自 Port Mortem 2026:作者在 72 小時內,嘗試把 JavaScript 的 glob 比對函式庫 Picomatch 移植到 Rust。原始文章可見:https://dev.to/lubuseb/i-ported-picomatch-to-rust-it-passed-1977-tests-and-lost-the-benchmark-by-18x-19fa。
來源事實是:作者鎖定 Picomatch 的特定版本,重寫掃描器、編譯器與比對器;原有 1,977 項測試全部通過,另有 28 項 Rust 原生測試。然而在相同的四組預先編譯模式與輸入上,Node 執行 Picomatch 的中位吞吐量約為每秒 718 萬次,Rust 原生 API 約為 39.5 萬次,慢約 18.2 倍;若經過同步 JavaScript 轉接層,更只剩約 1.16 萬次。
Picomatch 官方專案也證實,它不只是處理「*.js」的簡單萬用字元,而是支援 globstar、extglob、brace、POSIX bracket、否定模式、Windows 路徑與產生正規表示式等功能:https://github.com/micromatch/picomatch。因此,2,444 行 JavaScript 雖然看似不大,真正要複製的卻是多年累積的語意與歷史相容性。
運作機制:移植的不是語法,而是可觀察行為
glob 模式通常會被解析並編譯成正規表示式,再由引擎執行。問題在於,Picomatch 的公開 API 不只回傳「有沒有符合」;呼叫者還可能觀察產生出的正規表示式、擷取群組、UTF-16 索引、回呼順序、忽略規則與正規表示式旗標狀態。只要其中一項不同,即使普通檔名都能正確比對,也不能稱為相容。
最具代表性的漏洞是拉丁長 s「ſ」與 Kelvin 符號「K」。作者最初把不分大小寫視為單一 Unicode 問題,但 JavaScript 的舊式 /i 與 Unicode-aware 的 /iu 並不相同。ECMAScript 規範明確指出,Unicode 模式採用 simple case folding,因此「ſ」可對應 s、「K」可對應 k;非 Unicode 模式則刻意阻止部分非 ASCII 字元折疊到 ASCII:https://tc39.es/ecma262/2024/multipage/text-processing.html。這項第一方規範交叉驗證了原文描述的核心差異。
作者因而不只新增兩個範例,而是走訪全部 65,536 個 BMP UTF-16 code unit,建立 legacy Canonicalize 對照,再針對 literal 與 character class 執行 4,784 次檢查。這裡可合理推論:好的回歸測試不應只封住已知輸入,而應把單一失敗提升為一整個、範圍清楚的性質驗證。
為何現在值得注意
這不是單純的 Rust 效能趣聞。活動主辦方把任務定義為 AI 輔助跨語言移植,並要求保留功能、測試、基準與架構決策;評審項目甚至把功能可靠性與行為等價合計列為主要比重:https://portmortem.devfolio.co/。在生成式 AI 能快速產出「可編譯、測試全綠」程式碼的時代,瓶頸已從打字速度轉向證據品質。
原文採取的證據鏈包括:比對上游 38 個測試與 fixture 檔案、保留 1,977 項原始測試、執行 100,000 組固定種子的差異測試,再把 107 個已知邊界案例於各種子重播,共形成 100,535 次零差異執行。這仍不是數學上的完整證明,但比「我的測試都過了」更能說明測過什麼、如何重現,以及保證停在哪裡。
實際影響:慢,不代表毫無價值
原文對效能落差的解釋是:V8 已有高度最佳化的正規表示式引擎;Rust 版本採用帶有明確工作量配額的安全 ECMAScript 直譯器,而轉接層還增加喚醒 worker、IPC、封包處理、快取查找與 JavaScript 物件重建成本。合理推論是,語言本身並不直接決定速度;執行引擎、演算法、資料邊界與呼叫路徑才是基準結果的主要成分。
Rust 版真正新增的價值,是可脫離 Node 執行,以及在正規表示式工作量超出安全上限時回傳明確錯誤,而非偽裝成「沒有符合」。對建置工具而言,兩者差異很大:前者代表無法安全完成,後者則可能導致檔案被錯誤納入或排除。這是可靠性與故障語意的改善,不是吞吐量勝利。
容易忽略的限制
首先,18.2 倍只適用於作者指定的四組模式、輸入、Node 版本與測試環境;它不能外推成「Rust 比 JavaScript 慢 18 倍」。本文也未獨立重跑其程式,因此測試總數與效能數字應視為作者報告,而非第三方複驗結果。其次,固定種子的差異測試雖可重現,仍可能漏掉生成器未涵蓋的語法組合。最後,轉接層效能不能代表純 Rust API,反之亦然;部署架構不同,成本模型也會改變。
對台灣開發團隊的意義
台灣企業常在舊系統現代化、供應鏈安全或降低執行環境依賴時評估跨語言重寫。作者觀點是:決策起點不該是「Rust 理應比較快」,而應先寫清楚目標究竟是吞吐量、可攜性、記憶體安全、可預測的失敗,還是移除某個 runtime。若唯一目標是速度,本案目前沒有商業上的勝利;若需求是獨立執行與受控資源耗用,較慢的版本仍可能有部署價值。
實務上,專案應在全面重寫前先建立代表真實流量的基準、來源與目標版本的差異測試,以及可退出的停損條件。尤其 AI 產生的移植程式碼,審查重點應從「看起來像原語言」轉為「能否提出可重現的等價證據」。
後續觀察指標
- 是否有獨立開發者在不同硬體與真實 glob 工作負載上重現效能結果。
- 差異測試新增輸入後,零 mismatch 能否持續,並涵蓋非 BMP 字元及更多旗標組合。
- Rust 引擎經剖析與最佳化後,吞吐量、尾端延遲和記憶體占用如何變化。
- 工作量上限在正常大型專案中是否造成誤判,以及錯誤是否能被上層工具正確處理。
- 專案能否持續追蹤 Picomatch 上游更新,而非只相容於一次凍結的版本。
這個案例最重要的提醒不是「別用 Rust」,而是重寫工程有三條彼此獨立的軸線:測試通過、語意等價與效能等價。綠燈只能證明已執行的檢查沒有失敗;真正可靠的移植,還必須交代版本來源、比較邊界、未涵蓋範圍,以及那些不漂亮但會改變決策的數字。