Rust 面試:如何把使用 gen 識別字的 crate 遷移到 Rust 2024?
題幹與適用場景
團隊把多個 crate 從 Rust 2021 遷移到 Rust 2024。程式中有函式、模組、欄位、macro 產生的識別字 gen,而 Rust 2024 將 gen 保留為關鍵字,導致部分 crate 在新 edition 解析失敗。請設計偵測、修復、CI 閘門和回滾方案。
題目聚焦 edition 遷移,不假設 gen blocks 已經穩定,也不把 compiler 升級與 edition 切換混為一談。
面試官考察點
- 能否說明 edition 影響解析規則,但不同 edition 的 crate 仍可互相依賴。
- 能否使用
keywordidents2024lint 和cargo fix --edition找出 gen 識別字。 - 能否區分重新命名與 raw identifier 的相容性取捨。
- 能否涵蓋 macro、公開 API、build script、測試與 workspace 相依關係。
回答前需要澄清的問題
- workspace 目前各 crate 的 edition、MSRV 和 compiler 版本是什麼?
gen是否出現在公開 API、序列化名稱或外部 macro 介面?- 受影響識別字來自原始碼、proc macro 生成碼,還是 build script?
- 遷移期間需要同時支援 Rust 2021 與 2024 嗎?
- CI 是否能在兩個 edition 與多個 feature 組合上執行測試?
30 秒回答框架
「我先盤點 workspace 的 edition、工具鏈和所有 gen 來源,再啟用 keywordidents2024 lint。對內部名稱優先重新命名;必須保留舊 API 時使用 raw identifier r#gen,並確認呼叫端在兩個 edition 都能解析。用 cargo fix --edition 產生機械修復後逐個 review macro 和公開介面,手動更新 Cargo manifest,再在 Rust 2021、2024、完整 feature 矩陣跑 check、test、doc 和 lint。每個 crate 分批切換,保留可回滾提交與相依套件相容報告。」
分步驟深入解答
1. 先分離 compiler 與 edition 變更
Rust compiler 可以編譯既有 edition;edition 是 crate 在解析時選擇的語言規則。先固定工具鏈與 lockfile,建立 Rust 2021 基線,再只改一個 crate 的 edition 欄位,避免把 compiler、依賴升級和語意變更混在同一個 diff。
2. 找出所有 gen 來源
啟用 keywordidents2024,讓 lint 把在舊 edition 合法、在 2024 會衝突的識別字標出。靜態搜尋不能漏掉 macro 展開、proc macro 生成的 token、build script 和測試;對生成碼要在生成前修模板,不能只改產物。
3. 選擇改名或 raw identifier
內部函式、區域變數和模組可直接改名,降低長期閱讀成本。若公開 API、序列化名稱或跨 crate 呼叫必須保留 gen,可使用 raw identifier,讓程式碼在需要的 edition 仍指向同一個符號。這是遷移橋接,不代表應永久保留不清晰的命名。
pub fn r#gen() -> IteratorType {
// implementation
}
fn call() {
let _items = r#gen();
}4. 使用自動修復但保留人工審查
cargo fix --edition 會把相關 lint 提升為 warning 並套用 compiler 建議,例如把 gen 改成 r#gen;它不會替你完成所有產品命名決策,也不應直接覆蓋未提交工作。先在乾淨分支執行,檢查 macro、doc test、生成碼和公開 API diff,再手動更新 manifest 的 edition。
5. 驗證跨 edition 邊界
Rust 不同 edition 的 crate 可以互相連結,因此先在 workspace 內逐 crate 遷移。CI 至少執行 2021 與 2024 的 cargo check、cargo test、cargo doc、clippy 和 feature 組合;對公開 API 加入編譯型範例,確認呼叫端不會因 raw identifier 或重新命名而失敗。
6. 觀測、分批與回滾
記錄 lint 命中數、edition、toolchain、crate、feature、macro 來源和失敗測試,禁止把原始憑證或敏感輸入寫入日誌。先遷移葉子 crate,再遷移被多方依賴的 crate;每批保留舊版 tag、lockfile 與可重現建置,發現下游破壞時回退整批而不是手動混合部分修復。
高品質示範回答
「edition 改變解析規則,compiler 升級則是另一個變數;我先固定工具鏈並建立 Rust 2021 基線。接著啟用 keywordidents2024,盤點原始碼、macro、proc macro、build script 和測試中的 gen。內部符號直接改名,公開 API 若需保留名稱則使用 r#gen,並檢查序列化與跨 crate 呼叫。用 cargo fix --edition 做機械修復後 review diff,手動更新 manifest,最後在 2021/2024 和 feature 矩陣跑 check、test、doc、clippy。按依賴順序分批切換,記錄 lint 與失敗測試,保留可重現的回滾點。」
常見錯誤
- 只升級 compiler 就宣稱完成 → edition 解析規則仍未驗證 → 固定基線後單獨切換 edition。
- 只 grep 原始碼 → macro 或生成碼仍產生
gen→ 在 token 生成源頭加 lint 和測試。 - 全部名稱都改成 r#gen → 長期 API 可讀性下降 → 內部名稱改名,只有相容邊界保留 raw identifier。
- 直接接受 cargo fix diff → 公開 API 或文件範例可能被破壞 → 逐檔 review 並跑跨 edition CI。
- workspace 一次全切 → 難以定位下游回歸 → 按依賴拓撲分批並保留回滾點。
追問及應對
gen blocks 已經可以在 Rust 2024 使用了嗎?
gen 先被保留為關鍵字,目的是為未來 gen blocks 留出語法空間;題目中的遷移不能假設該功能已穩定。應以當前工具鏈文件和 feature 狀態為準。
raw identifier 會改變 ABI 或序列化名稱嗎?
raw identifier 主要改變 parser 對名稱的解讀;是否影響 ABI、反射、序列化或 FFI 要由實際公開符號和生成器檢查。對外協定應以明確的 wire name 與相容測試保護。
cargo fix 後為何還要手動改 Cargo.toml?
cargo fix --edition 會修正程式碼中的 lint 建議,但 edition 欄位仍需人工確認與提交。把 manifest 變更和測試結果一起 review,才能避免誤切換或遺漏 workspace crate。