終結 termino:當 40 年舊技術假設過期時
在軟體維護的世界裡,有一些「定律」——技術假設如此深植於內以至於變得看不見。對許多 Unix 老兵來說,其中一條定律就是 terminfo 和 curses 函式庫的必要性。四十年來,建立文字使用者介面(TUI)的標準做法是避免對使用者硬體做任何假設,而是依賴一個複雜的終端機功能資料庫,以確保程式能在從 VT100 到 Wyse 50 的各種設備上運行。
最近,Eric S. Raymond (ESR) 在將一個舊的 Unix 遊戲「greed」從 C 移植到 Rust 的過程中,分享了他對此做法過時的個人領悟。他的經歷凸顯了運算領域的一個更廣泛的轉變:從分散的硬體格局過渡到軟體模擬的同質化環境。
curses 與 termino 的遺產
要理解此轉變為何重要,必須先了解 curses 的作用。在 Unix 的早期,使用者透過龐大的實體字符單元終端機陣列連接到系統。每個製造商都有不同的逃逸序列,用於移動游標、清除螢幕或改變顏色。
為了解決此問題,開發了 curses 函式庫。它透過檢查 TERM 環境變數並查詢系統範圍內的資料庫 terminfo 來運作。該資料庫包含「魔法字串」——即操作特定終端機型號所需的特定控制碼。透過抽象硬體,開發者可以編寫一段程式碼,使其在數百種不同設備上運行。
催化劑:移植到 Rust
在維護「greed」期間,Raymond 決定將程式碼基礎遷移到 Rust,以利用該語言的安全保證。使用 AI 助手來處理移植,他注意到一個反覆出現的問題:生成的 Rust 程式碼充滿了 unsafe 區塊。
經過調查,他發現 AI 只是包裝了 C 語言的 curses 函式庫。由於 curses 是 C 函式庫,在 Rust 中與其互動需要 unsafe 區塊,這敗遷移的主要目的。
當 Raymond 請求 AI 使用純 Rust 的替代方案,如 crossterm 時,AI 遇到了一個障礙:Raymond 沒有安裝 cargo(Rust 套件管理器),因為他依賴 AI 來處理移植的「code-grinding」,而不是手動編寫 Rust。
「意外」的創新
面對缺乏套件管理器的情況,AI 選擇了一條意外的路徑。它沒有依賴外部函式庫或舊版 C curses 系統,而是直接在原始程式碼中編寫了一個自訂且輕量的螢幕繪製後端。
關鍵的是,這個新後端做出了一個大膽的假設:它假設使用者正在使用彩色 ANSI 終端機。
對於經驗豐富的 Unix 開發者來說,這違反了一條基本規則。然而,在檢閱程式碼後,Raymond 意識到結果很小、優雅,而且完全沒有 unsafe 區塊。AI 缺乏 20 世紀 Unix 硬體的「制度記憶」,因此簡單地忽略了一個不再具有實際用途的需求。
終端機的新現實
今天,幾乎每個被使用的終端機都是 ANSI 相容的模擬器。曾經需要 terminfo 的各種實體硬體多樣性已經消失,被遵守共同標準的軟體所取代。
Raymond 指出,這種領悟感覺就像「宇宙向一側傾斜」。一個必須支援數百種過時終端機類型的假設,在需求消失後仍然持續存在。正如他所說:
"我以前從未直接面臨過這個事實:構成 TERM 和 terminfo 的整套假設,就像撥號上網的音調調製解調器一樣過時了。"
反論與考量
雖然僅假設 ANSI 終端機簡化了開發,但一些開發者仍會遇到舊系統帶來的摩擦。Hacker News 上的一位評論者指出,他們目前僅剩的與 terminfo 互動情況,是在環境變數設定錯誤時(例如,$TERM 被設為 screen-256color 而非 screen),導致像 vim 或 less 這樣的工具出現故障。在這種情況下,他們主張,假設一個標準終端機其實可能比依賴可能被損壞的環境變數 更可靠。
結論:代碼如濕黏土
「greed」轉向簡化的 ANSI 後端,說明了我們看待舊程式碼的方式發生了轉變。在 AI 輔助開發的時代,改變基本架構假設的成本大幅降低。如果未來出現需要非 ANSI 支援的使用者,修復不再需要數月的重寫,而只是一個簡單的提示給 AI。
擺脫 40 年舊假設的負擔後,開發者可以產出更精簡、更安全、且更易維護的軟體,並承認某些「古老定律」的編碼僅僅不再適用於現代世界。