[TOOLS] 15 分鐘閱讀OraCore 編輯部

Rust 論壇週報把想法變交付

我把 Rust 論壇 week 25 拆成可抄的方法:怎麼用測試、命名、狀態隔離和小模板,把粗想法變成能交付的工具。

分享 LinkedIn
Rust 論壇週報把想法變交付

我把這篇 Rust 論壇週報拆成可直接抄的做法:測試生成器、隔離 GUI 狀態、整理錯誤 crate、還有把小工具做成真的能用。

我看 Rust 論壇每週進度串看了一陣子,這篇最對味。不是那種「我發了版,大家鼓掌」的貼文,而是很誠實地把卡住的地方攤開來:生成器要補測試、GUI 要跟 daemon 對話、錯誤 crate 想保留型別又不想把自己寫成一坨、還有一個只花幾小時的小 applet,結果反而真的有人會用。這種內容我最愛,因為它不像簡報,像是在承認:工具從來不是做完就好,是做得住才算數。

我拆的是 Rust Programming Language Forum 的 What’s everyone working on this week (25/2026)?。這串沒有單一結論,反而更有料。你可以直接看到不同 Rust 專案怎麼脫困:加測試、把狀態切乾淨、少用 unsafe、把 IPC event 命名講清楚,還有別再假裝「小工具」不會長出真正的結構。

1) 生成器要先能驗證,不然你只是會吐檔案

訂閱 AI 趨勢週報

每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。

不會寄垃圾信,隨時可取消。

"I'm continuing to work on xidl , a code generator for generate HTTP server and client code. I recently added BDD tests to xidl to ensure consistency across multi-language implementations."

翻譯一下就是:如果你的工具會輸出多種語言的程式碼,你就不能只靠肉眼看結果。xidl 的作者 cathaysia 加了 BDD tests,重點不是「有測試很棒」,而是他已經碰到真正的失敗模式:不同語言實作開始慢慢長歪。A 語言的預設 header 跟 B 語言不一樣,A 的錯誤處理跟 B 的 retry 路徑不同,最後你以為自己在做 generator,其實是在養一台漂移機器。

Rust 論壇週報把想法變交付

我自己也踩過這種坑。第一版工具都很順,因為你只支援一個 target,還能手動看輸出。問題是使用者一旦要求第二個 runtime,整個專案就變形了。它不再只是「產生檔案」,而是「保證各 target 行為一致」。這時候如果沒有測試,你根本不知道哪個版本才是正確答案。

BDD 在這裡特別適合,因為你不是在驗單一函式,而是在驗場景:同一份 schema 丟進去,server/client artifacts 出來,而且跨 target 的行為要一致。這種測試很土,但很有效。比起堆一堆抽象,我寧願先看到三個會真的踩到的情境。

實操寫法我會這樣做:

  • 先挑真實 API 形狀做 fixture,不要只做理想範例。
  • 同一份 input,對所有支援 target 跑一遍。
  • 比對行為,不只比對檔案有沒有生成。
  • 先鎖住最容易分歧的地方:optional fields、streaming、auth、retries。

如果你在做 generator,我寧可看見三個醜但完整的 BDD scenario,也不要看見五十個沒驗證的抽象層。scenario 會告訴我,你到底以為這工具在幹嘛。

2) 你以為是移植,其實是重寫加決策

"Translating a 1978 BASIC program to rust - Lunar Lander . The original is only 70 loc - the rust version is 3-4 times longer - not counting a genetic algorithm I'm adding to find optimal landing sequences."

這段很誠實,也很刺眼。原本 BASIC 只有 70 行,Rust 版本已經長到 3 到 4 倍,還沒算上作者要加的 genetic algorithm。這不是 Rust 膨脹,是現實本來就這樣。你一旦從「短短一段腳本」變成「型別清楚、可維護、可擴充」的版本,行數就會上去。你再加搜尋或最佳化,行數繼續上去。這不是壞事,這是代價。

也就是說,移植從來不是純搬家。你是在翻譯原始行為,還要順手把原本藏在程式形狀裡的假設攤開。BASIC 版可能把很多東西寫死了,在 Rust 裡你得明確決定:單位怎麼定、狀態放哪、怎麼保證模擬可重現。這些都不是「額外工作」,這些就是把程式變成專案的那一段。

我以前重寫過一個小型 physics toy,原始版本在 script 語言裡很快就能跑。搬到 Rust 之後,光是決定單位系統、狀態邊界、deterministic 行為,就花了不少時間。可是當我後面真的要加 search、tuning、比較不同策略時,這些結構全部都救了我。原本看起來像拖慢進度的東西,後來都變成可以繼續擴的骨架。

我會這樣實操:

  • 把「忠實移植」和「新增功能」切成兩個明確階段。
  • 保留原始行為的測試殼,不要直接拿新功能蓋過去。
  • 把新演算法獨立成 module,不要塞滿 helper function。
  • 先驗 correctness,再談優化搜尋路徑。

所以我看這種專案時,不會問「怎麼比原本長這麼多」。我會問:你有沒有把原本隱藏的假設,變成現在看得懂、能改、能測的東西。

3) GUI 不是畫面問題,是事件路由問題

"Polishing ringdrop-gui , the Tauri v2 frontend for ringdrop daemon. This week: adapting to a new daemon IPC event ( FileProgress ) during directory transfers, and namespacing progress channels by blob hash so concurrent downloads are possible."

這段我一看就懂,因為這種 bug 很煩。Tauri v2 前端不是單純補 UI,而是要跟 daemon 的 IPC event 對齊。重點在新事件 FileProgress,還有把 progress channel 用 blob hash 做 namespace,這樣才能同時處理多個下載。這聽起來很小,但只要你做過多工下載,就知道不 namespace 的後果有多噁心:進度條互相污染、狀態被覆蓋、最後你根本不知道哪一筆在更新。

Rust 論壇週報把想法變交付

白話一點說,GUI state 其實就是 routing 問題。你如果沒替每個長任務配一個穩定 ID,就等於假設一次只能有一件事在跑。這種假設通常只活到第一個真使用者出現。daemon 發出事件、frontend 收到事件,接下來不是「顯示一下就好」,而是要能精準對回對應工作項目。blob hash 這種 key 的價值就在這裡:它讓事件有身份,不會亂串。

我以前做桌面工具也踩過一樣的坑。後端跑得比前端快,結果 UI 看起來像在抽風。不是 architecture 大錯,而是你把事件當廣播,沒把它當 scoped message。修法通常很無聊,但很有效:把 job、progress、error 全部綁在同一個 ID 上,然後別讓不同工作共用一個狀態槽。

實操寫法我會這樣做:

  • 每個長任務都給穩定 ID,不要靠陣列位置。
  • progress、log、error 都用同一個 ID namespace。
  • 新 IPC event 先當 state transition,不要先當通知。
  • 先測兩個 concurrent jobs,再說你完成了。

這種東西最容易被低估。等你真的讓兩個下載同時跑起來,才會知道「把事件分清楚」不是優化,是保命。

4) 小工具能活下來,靠的是位置,不是雄心

"Theme mode applet for cosmic Hi, I made an applet for cosmic DE to switch between light and dark mode easily from the taskbar. Only a few hours work but I think it may be useful."

這個 COSMIC DE applet 很樸素,但我覺得很對。從 taskbar 一鍵切 light/dark mode,功能小到不能再小,卻剛好打中一個重複動作。這種工具最常被錯估,因為大家總覺得價值要來自複雜度。其實不是。很多時候價值來自你把控制放在對的位置,讓使用者不用再去設定頁裡翻半天。

也就是說,小 applet 的重點不是它有多厲害,而是它是不是貼著高頻動作。你每天都要切 theme,那就別把開關藏起來。你每次都要找三層選單,那這工具就算做得再漂亮也沒用。對使用者來說,省一次滑鼠移動,比多一個 fancy 功能實際太多了。

我自己也做過這種小桌面 helper。寫的時候常常覺得:「這也太小了吧?」但真正上手之後才發現,interaction design 比 code 本身重要。只要它在對的地方,使用者就會把它變成習慣。反過來說,UI 再花俏,只要多一個步驟,大家就懶得碰。

我會這樣實操:

  • 只挑一個高頻動作,先把它做到底。
  • 把 UI surface 壓到最小。
  • 讓 applet 重啟後不用重設。
  • 先 ship 可用版本,再慢慢加偏好設定。

如果我是同事,我只會問一件事:這個東西是不是每天都幫我省時間?如果答案是 yes,它就不是 demo,它是工具。

5) Error crate 真正難的是:typed 和 erased 不能互相裝死

"Polishing erratic, my error handling crate . It composes nicely between type-erased errors and typed error states, and uses pointer tagging to eliminate allocations whenever possible. To reduce the number of unsafe , I plan to find (or write) something like GhostCell in the next couple of days to ensure vtable functions can only be invoked with the correct type-erased self ."

這段很 Rust,也很像真的在解問題。erratic 這個 error handling crate 想同時支援 type-erased errors 和 typed error states,還想用 pointer tagging 盡量減少 allocation。這不是在炫技,而是在處理真實世界裡兩種不同的錯誤資料:一種要保留精準型別,讓呼叫端知道到底壞在哪;另一種要能擦掉型別細節,把 context 帶著走。只支援其中一種,通常都不夠。

翻譯一下就是:錯誤處理不要假裝所有 error 都長一樣。真的系統裡,有些錯誤是 recoverable state,有些是傳遞型 context。你如果把它們混成一團,API 會很順,但使用者會很痛。反過來,如果你把型別都保留到底,API 也可能變得難用到沒人想碰。這就是這類 crate 的難點:既要有結構,又不能把人逼瘋。

我很在意作者提到 pointer tagging,因為這代表他不是只想把介面寫漂亮,還在看成本。allocation 不是每次都致命,但它會累積。只是 unsafe 也不能亂放。既然這個 crate 牽涉 type-erased self reference,那 invariant 就得很清楚,不然很容易寫出「看起來很安全、其實很危險」的東西。去參考 GhostCell 這種模型是合理的,至少你在想的是如何把 identity 和 borrow 規則講清楚,而不是只靠註解撐場面。

我之前被一個 error abstraction 搞過。happy path 很漂亮,失敗路徑卻完全看不懂。最後我學到的是:先決定哪些東西一定要 typed,哪些可以 erased,然後看使用者在邊界上到底需要什麼。只要 boundary 的語意不清楚,整個 crate 再優雅都沒用。

實操寫法我會這樣做:

  • 把可恢復的 typed state,和傳遞用的 erased error 分開設計。
  • 先量成本,再決定要不要為 allocation 做優化。
  • unsafe 只留一個很小的 boundary,還要寫清楚 invariant。
  • 同時寫「簡單錯誤」和「複雜 state machine」兩種 example。

如果一個 error crate 連自己的例子都講不清楚,我不會信。它如果能同時講懂兩種世界,我才會開始認真看。

6) Agent framework 最怕假裝自己很穩,所以先把混亂納進來

"I've recently come up with a new idea to optimize my own agent framework : integrate chaos engineering into the agent system. That should be really exciting."

這個提案我看了停一下。把 chaos engineering 放進 agent framework,聽起來不是瘋,就是很懂。可能兩個都有。因為 agent 系統本來就不是純函式,它依賴 tools、prompts、retries、memory、外部服務,任何一個地方抖一下,整條 pipeline 都可能開始亂猜。你如果只測順風局,就會把自己的系統想得太穩。

白話講,chaos engineering 的意思就是:別等真實故障來打臉,先自己丟一點受控失敗進去,看它怎麼壞。放到 agent 系統裡,可以是 tool response 延遲、context 被截斷、輸出格式壞掉、partial state corruption,甚至是某一步直接掉包。這些都不是理論題,是實際會發生的事。

我碰過不少 agent-ish 的流程,表面上每一步都能跑,真到某個 dependency 抽風時,整個系統就開始 improvising。偶爾這種 improvisation 有用,但更多時候只是 silent drift。把 failure injection 直接放進 framework,可以逼你正視:這套系統到底有沒有 recovery policy,還是只是靠運氣。

但我也會很小心。chaos 只有在可觀測、可重現、可回放時才有價值。你要知道故障是在哪裡注入的、agent 怎麼回應、最後怎麼恢復。不然你不是在測 resilience,你是在製造更多 debug 噪音。

實操寫法我會這樣做:

  • 一次只注入一種 failure mode。
  • 把注入點和 recovery path 都記錄下來。
  • 觀察 agent 是 retry、卡住,還是自己掰一個答案。
  • 讓 chaos layer 預設可關,而且測試時要 deterministic。

如果框架能撐過受控混亂,它才比較像真的能上線。撐不過,那就別急著把它包裝成智慧系統。

可抄的模板

# Rust weekly progress note template for shipping work

## What I worked on
- [Project name]
- [One sentence on the current goal]

## What changed this week
- [Concrete change #1]
- [Concrete change #2]
- [Concrete change #3]

## What broke or felt off
- [Bug, design issue, or friction point]
- [Why it happened]
- [What I changed to address it]

## What I’m testing now
- [Test type: BDD, unit, integration, manual]
- [Scenario or behavior being verified]
- [Edge cases that still worry me]

## How I’m handling state and identity
- [How events/jobs/records are keyed]
- [How concurrency is isolated]
- [How I avoid mixing unrelated work]

## What I’m keeping small
- [One thing I refused to overbuild]
- [One API or UI surface kept narrow]
- [One unsafe or complex boundary kept tiny]

## Next step
- [The next concrete action]
- [The risk I want to answer]
- [The minimal version I’ll ship]

## One-line summary
I’m building [thing] by [approach], and this week I learned [specific lesson].

這篇的原始來源是 Rust Forum thread:https://users.rust-lang.org/t/whats-everyone-working-on-this-week-25-2026/140724。上面拆法是我根據原文整理出的工作方法,模板是我另外整理成可直接抄的版本。